gaoluyang
2 天以前 895f483e19154752bae98ad29342b6555c5b614b
feat(hrm/transfer): 调岗申请支持选择员工并自动带出原部门原岗位

- 新增弹框增加「申请人」下拉,可按姓名搜索选择调岗员工,过滤未绑账号/已离职人员
- 选择员工后自动带出原部门、原岗位;目标部门/岗位与其相同时自动清空,避免被后端拦截
- 列表「申请人」列改取 userName,修复该列一直为空的问题
- 弹框宽度由 600px 调整为 820px,调岗类型按钮不再折行

Co-Authored-By: Claude Code <noreply@anthropic.com>
已修改3个文件
109 ■■■■■ 文件已修改
src/api/hrm/transfer/index.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/transfer/data.ts 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/transfer/modules/form.vue 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/hrm/transfer/index.ts
@@ -7,8 +7,10 @@
  /** 调岗申请 */
  export interface Transfer {
    id?: number;
    employeeId?: number;
    employeeName?: string;
    /** 申请人(员工关联的系统用户 ID) */
    userId?: number;
    /** 申请人姓名 */
    userName?: string;
    currentDeptId?: number;
    currentDeptName?: string;
    currentPostId?: number;
src/views/hrm/transfer/data.ts
@@ -7,13 +7,18 @@
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',
@@ -40,12 +45,45 @@
      },
    },
    {
      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: '选择员工后自动带出',
      },
    },
    {
@@ -54,7 +92,7 @@
      component: 'Input',
      componentProps: {
        disabled: true,
        placeholder: '自动获取',
        placeholder: '选择员工后自动带出',
      },
    },
    {
@@ -175,7 +213,7 @@
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 },
src/views/hrm/transfer/modules/form.vue
@@ -1,6 +1,7 @@
<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';
@@ -10,14 +11,12 @@
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(() => {
@@ -41,6 +40,36 @@
  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) {
@@ -53,7 +82,6 @@
    }
    modalApi.lock();
    const data = (await formApi.getValues()) as HrmTransferApi.Transfer;
    data.employeeId = currentEmployeeId.value;
    try {
      if (data.id) {
        await updateTransfer(data);
@@ -73,27 +101,22 @@
    }
    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 => ({
@@ -112,7 +135,7 @@
</script>
<template>
  <Modal :title="getTitle" class="w-[600px]">
  <Modal :title="getTitle" class="w-[820px]">
    <Form class="mx-4" />
  </Modal>
</template>