已修改9个文件
254 ■■■■■ 文件已修改
src/api/hrm/resignation/index.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/hrm/transfer/index.ts 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/resignation/index.vue 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/schedule/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/transfer/data.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/transfer/detail/approve/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/transfer/index.vue 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/hrm/transfer/modules/form.vue 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/components/select-dialog.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/hrm/resignation/index.ts
@@ -24,6 +24,12 @@
    remark?: string;
    createTime?: string;
  }
  /** 审批流程 */
  export interface ApproveProcess {
    key: string;
    name: string;
  }
}
/** 查询离职申请分页列表 */
@@ -57,8 +63,10 @@
}
/** 提交离职申请(发起审批) */
export function submitResignation(id: number) {
  return requestClient.post(`/hrm/resignation/submit?id=${id}`);
export function submitResignation(id: number, processDefinitionKey: string) {
  return requestClient.post('/hrm/resignation/submit', null, {
    params: { id, processDefinitionKey },
  });
}
/** 撤销离职申请 */
@@ -73,5 +81,7 @@
/** 获取可用流程定义列表 */
export function getResignationApproveProcessDefinitionList() {
  return requestClient.get<any[]>('/hrm/resignation/approve-process-definition-list');
  return requestClient.get<HrmResignationApi.ApproveProcess[]>(
    '/hrm/resignation/approve-process-definition-list',
  );
}
src/api/hrm/transfer/index.ts
@@ -7,8 +7,8 @@
  /** 调岗申请 */
  export interface Transfer {
    id?: number;
    employeeId?: number;
    employeeName?: string;
    userId?: number;
    userName?: string;
    currentDeptId?: number;
    currentDeptName?: string;
    currentPostId?: number;
@@ -26,6 +26,12 @@
    processInstanceId?: string;
    remark?: string;
    createTime?: string;
  }
  /** 审批流程 */
  export interface ApproveProcess {
    key: string;
    name: string;
  }
}
@@ -58,8 +64,10 @@
}
/** 提交调岗申请(发起审批) */
export function submitTransfer(id: number) {
  return requestClient.post(`/hrm/transfer/submit?id=${id}`);
export function submitTransfer(id: number, processDefinitionKey: string) {
  return requestClient.post('/hrm/transfer/submit', null, {
    params: { id, processDefinitionKey },
  });
}
/** 撤销调岗申请 */
@@ -69,5 +77,7 @@
/** 获取可用流程定义列表 */
export function getTransferApproveProcessDefinitionList() {
  return requestClient.get<any[]>('/hrm/transfer/approve-process-definition-list');
  return requestClient.get<HrmTransferApi.ApproveProcess[]>(
    '/hrm/transfer/approve-process-definition-list',
  );
}
src/views/hrm/resignation/index.vue
@@ -2,14 +2,17 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { HrmResignationApi } from '#/api/hrm/resignation';
import { ref } from 'vue';
import { Page, useVbenModal } from '#/packages/effects/common-ui/src';
import { message } from 'ant-design-vue';
import { message, Modal, Select } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
  cancelResignation,
  deleteResignation,
  getResignationApproveProcessDefinitionList,
  getResignationPage,
  submitResignation,
  updateHandoverStatus,
@@ -59,11 +62,34 @@
  }
}
/** 提交离职申请 */
const submitModalVisible = ref(false);
const currentRow = ref<HrmResignationApi.Resignation>();
const processList = ref<HrmResignationApi.ApproveProcess[]>([]);
const selectedProcessKey = ref<string>();
const submitLoading = ref(false);
/** 提交离职申请(先选择审批流程) */
async function handleSubmit(row: HrmResignationApi.Resignation) {
  await submitResignation(row.id!);
  message.success('提交成功');
  handleRefresh();
  processList.value = await getResignationApproveProcessDefinitionList();
  currentRow.value = row;
  selectedProcessKey.value = undefined;
  submitModalVisible.value = true;
}
async function confirmSubmit() {
  if (!selectedProcessKey.value) {
    message.warning('请选择离职审批流程');
    return;
  }
  submitLoading.value = true;
  try {
    await submitResignation(currentRow.value!.id!, selectedProcessKey.value);
    message.success('提交成功');
    submitModalVisible.value = false;
    handleRefresh();
  } finally {
    submitLoading.value = false;
  }
}
/** 撤销离职申请 */
@@ -186,5 +212,19 @@
        />
      </template>
    </Grid>
    <Modal
      v-model:open="submitModalVisible"
      title="提交至协同办公"
      width="420px"
      :confirm-loading="submitLoading"
      @ok="confirmSubmit"
    >
      <Select
        v-model:value="selectedProcessKey"
        class="w-full"
        placeholder="请选择离职审批流程"
        :options="processList.map((item) => ({ label: item.name, value: item.key }))"
      />
    </Modal>
  </Page>
</template>
src/views/hrm/schedule/index.vue
@@ -522,7 +522,18 @@
        "
        class="py-20"
      />
      <table v-else class="schedule-table w-full text-sm">
      <table v-else class="schedule-table text-sm">
        <!-- 勾选列 40px、员工列 160px 固定;日期列按视图模式定宽 -->
        <colgroup>
          <col style="width: 40px" />
          <col style="width: 160px" />
          <!-- 周视图:日期列均分剩余宽度(保证班次名完整显示);月视图:固定 52px 横向滚动 -->
          <col
            v-for="vd in viewDays"
            :key="vd.date"
            :style="viewMode === 'month' ? 'width: 52px' : undefined"
          />
        </colgroup>
        <thead>
          <tr>
            <th class="schedule-check-col bg-gray-100">
@@ -679,6 +690,7 @@
  min-width: max-content;
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
  th,
  td {
@@ -687,7 +699,6 @@
  }
  .schedule-date-col {
    width: 52px;
    min-width: 52px;
  }
@@ -708,7 +719,8 @@
  .schedule-name-col {
    left: 40px;
    min-width: 180px;
    width: 160px;
    min-width: 160px;
    padding: 0 12px;
    line-height: 1.3;
  }
@@ -734,7 +746,7 @@
    padding: 1px 6px;
    overflow: hidden;
    color: #fff;
    font-size: 11px;
    font-size: 12px;
    text-overflow: ellipsis;
    white-space: nowrap;
    border-radius: 3px;
src/views/hrm/transfer/data.ts
@@ -1,6 +1,9 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { HrmTransferApi } from '#/api/hrm/transfer';
import type { SystemUserApi } from '#/api/system/user';
import { markRaw } from 'vue';
import { DICT_TYPE } from '#/packages/constants/src';
import { getDictOptions } from '#/packages/effects/hooks/src';
@@ -8,12 +11,16 @@
import { getDeptList } from '#/api/system/dept';
import { getSimplePostList } from '#/api/system/post';
import { handleTree } from '#/packages/utils/src';
import { UserSelect } from '#/views/system/user/components';
/** 表单类型 */
export type FormType = 'create' | 'detail' | 'update';
/** 新增/修改调岗申请的表单 */
export function useFormSchema(formType: FormType): VbenFormSchema[] {
export function useFormSchema(
  formType: FormType,
  onUserChange?: (user?: SystemUserApi.User) => void,
): VbenFormSchema[] {
  return [
    {
      fieldName: 'id',
@@ -22,6 +29,16 @@
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'userId',
      label: '调岗人员',
      component: markRaw(UserSelect),
      componentProps: {
        placeholder: '请选择调岗人员',
        onChange: onUserChange,
      },
      rules: 'selectRequired',
    },
    {
      fieldName: 'currentDeptId',
@@ -175,7 +192,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/detail/approve/index.vue
@@ -30,7 +30,7 @@
  bordered: true,
  column: 2,
  schema: [
    { label: '员工姓名', field: 'employeeName', render: textRender },
    { label: '调岗人员', field: 'userName', render: textRender },
    { label: '当前部门', field: 'currentDeptName', render: textRender },
    { label: '当前职位', field: 'currentPostName', render: textRender },
    { label: '目标部门', field: 'targetDeptName', render: textRender },
src/views/hrm/transfer/index.vue
@@ -2,14 +2,17 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { HrmTransferApi } from '#/api/hrm/transfer';
import { ref } from 'vue';
import { Page, useVbenModal } from '#/packages/effects/common-ui/src';
import { message } from 'ant-design-vue';
import { message, Modal, Select } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
  cancelTransfer,
  deleteTransfer,
  getTransferApproveProcessDefinitionList,
  getTransferPage,
  submitTransfer,
} from '#/api/hrm/transfer';
@@ -58,11 +61,34 @@
  }
}
/** 提交调岗申请 */
const submitModalVisible = ref(false);
const currentRow = ref<HrmTransferApi.Transfer>();
const processList = ref<HrmTransferApi.ApproveProcess[]>([]);
const selectedProcessKey = ref<string>();
const submitLoading = ref(false);
/** 提交调岗申请(先选择审批流程) */
async function handleSubmit(row: HrmTransferApi.Transfer) {
  await submitTransfer(row.id!);
  message.success('提交成功');
  handleRefresh();
  processList.value = await getTransferApproveProcessDefinitionList();
  currentRow.value = row;
  selectedProcessKey.value = undefined;
  submitModalVisible.value = true;
}
async function confirmSubmit() {
  if (!selectedProcessKey.value) {
    message.warning('请选择调岗审批流程');
    return;
  }
  submitLoading.value = true;
  try {
    await submitTransfer(currentRow.value!.id!, selectedProcessKey.value);
    message.success('提交成功');
    submitModalVisible.value = false;
    handleRefresh();
  } finally {
    submitLoading.value = false;
  }
}
/** 撤销调岗申请 */
@@ -165,5 +191,19 @@
        />
      </template>
    </Grid>
    <Modal
      v-model:open="submitModalVisible"
      title="提交至协同办公"
      width="420px"
      :confirm-loading="submitLoading"
      @ok="confirmSubmit"
    >
      <Select
        v-model:value="selectedProcessKey"
        class="w-full"
        placeholder="请选择调岗审批流程"
        :options="processList.map((item) => ({ label: item.name, value: item.key }))"
      />
    </Modal>
  </Page>
</template>
src/views/hrm/transfer/modules/form.vue
@@ -1,8 +1,12 @@
<script lang="ts" setup>
import type { FormType } from '../data';
import type { HrmTransferApi } from '#/api/hrm/transfer';
import type { SystemPostApi } from '#/api/system/post';
import type { SystemUserApi } from '#/api/system/user';
import { computed, ref } from 'vue';
import { useUserStore } from '@vben/stores';
import { useVbenModal } from '#/packages/effects/common-ui/src';
@@ -10,14 +14,15 @@
import { useVbenForm } from '#/adapter/form';
import { createTransfer, getTransfer, updateTransfer } from '#/api/hrm/transfer';
import { getEmployeeSimpleList } from '#/api/hrm/employee';
import { getSimplePostList } from '#/api/system/post';
import { getUser } from '#/api/system/user';
import { $t } from '#/locales';
import { useFormSchema } from '../data';
const emit = defineEmits(['success']);
const formType = ref<FormType>('create');
const currentEmployeeId = ref<number>();
const userStore = useUserStore();
const isDetail = computed(() => formType.value === 'detail');
const getTitle = computed(() => {
@@ -41,6 +46,44 @@
  showDefaultActions: false,
});
/** 岗位列表缓存(用于把岗位 ID 回显成名称) */
const postList = ref<SystemPostApi.Post[]>([]);
/** 根据岗位编号取岗位名称 */
async function getPostName(postId?: number) {
  if (!postId) {
    return undefined;
  }
  if (postList.value.length === 0) {
    postList.value = await getSimplePostList();
  }
  return postList.value.find((item) => item.id === postId)?.name;
}
/** 选择调岗人员后,自动带出其原部门、原岗位 */
async function fillCurrentByUser(user?: SystemUserApi.User) {
  if (!user?.id) {
    await formApi.setValues({
      currentDeptId: undefined,
      currentDeptName: undefined,
      currentPostId: undefined,
      currentPostName: undefined,
    });
    return;
  }
  // 选择组件带回的数据可能缺少部门名称 / 岗位,按编号再查一次用户详情
  const detail = !user.deptName || !user.postIds?.length
    ? await getUser(user.id)
    : user;
  const postId = detail.postIds?.[0] ? Number(detail.postIds[0]) : undefined;
  await formApi.setValues({
    currentDeptId: detail.deptId,
    currentDeptName: detail.deptName,
    currentPostId: postId,
    currentPostName: await getPostName(postId),
  });
}
const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    if (isDetail.value) {
@@ -53,7 +96,6 @@
    }
    modalApi.lock();
    const data = (await formApi.getValues()) as HrmTransferApi.Transfer;
    data.employeeId = currentEmployeeId.value;
    try {
      if (data.id) {
        await updateTransfer(data);
@@ -73,27 +115,21 @@
    }
    const data = modalApi.getData<{ formType: FormType; id?: number }>();
    formType.value = data.formType;
    formApi.setState({ schema: useFormSchema(data.formType) });
    formApi.setState({ schema: useFormSchema(data.formType, fillCurrentByUser) });
    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,
        });
      // 新增时默认带出当前登录人,并回填其原部门 / 原岗位
      const loginUserId = userStore.userInfo?.id;
      if (loginUserId) {
        formApi.setFieldValue('userId', loginUserId);
        await fillCurrentByUser(await getUser(loginUserId));
      }
      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 +148,7 @@
</script>
<template>
  <Modal :title="getTitle" class="w-[600px]">
  <Modal :title="getTitle" class="w-[850px]">
    <Form class="mx-4" />
  </Modal>
</template>
</template>
src/views/system/user/components/select-dialog.vue
@@ -291,7 +291,10 @@
  >
    <Row :gutter="12">
      <Col :span="5">
        <DeptTreeSelect ref="deptTreeRef" @select="handleDeptNodeClick" />
        <!-- 部门树高度与右侧用户列表保持一致,超出部分内部滚动,避免撑高弹窗 -->
        <div class="h-[520px] overflow-y-auto pr-1">
          <DeptTreeSelect ref="deptTreeRef" @select="handleDeptNodeClick" />
        </div>
      </Col>
      <Col :span="19">
        <Grid table-title="用户列表" />