云
2026-09-29 07aab8bb335132d5dc56f2b000301457079e8539
src/views/research/application/modules/fund-form.vue
@@ -3,9 +3,6 @@
import { computed, ref } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { useVbenModal } from '@vben/common-ui';
import { Alert, message } from 'ant-design-vue';
@@ -22,6 +19,8 @@
const isEdit = ref(false);
/** 申请经费减去已登记拨付金额后的可用余额 */
const remainder = ref<number>();
/** 编辑「已驳回」记录时展示的财务驳回原因 */
const rejectReason = ref('');
const alertMessage = computed(() => {
  if (remainder.value === undefined) {
@@ -98,17 +97,6 @@
      },
    },
    {
      fieldName: 'status',
      label: '拨付状态',
      component: 'Select',
      componentProps: {
        options: getDictOptions(DICT_TYPE.RESEARCH_FUND_STATUS, 'number'),
        placeholder: '请选择拨付状态',
      },
      rules: 'selectRequired',
      help: '选择「已拨付」时必须上传拨付凭证附件',
    },
    {
      fieldName: 'remark',
      label: '备注',
      component: 'Textarea',
@@ -128,6 +116,8 @@
        multiple: true,
        showDescription: true,
      },
      rules: 'required',
      help: '必填:凭证将随自动生成的财务报销单一起送交财务审批',
      formItemClass: 'col-span-2',
    },
  ],
@@ -142,12 +132,9 @@
    }
    const values =
      (await formApi.getValues()) as ResearchFundRecordApi.FundRecord;
    // 「已拨付」必须有凭证,先在前端拦一道,避免用户白等一次请求
    if (
      values.status === FundStatus.PAID &&
      (!values.blobIds || values.blobIds.length === 0)
    ) {
      message.warning('拨付状态为「已拨付」时,请先上传拨付凭证附件');
    // 凭证必填,先在前端拦一道,避免用户白等一次请求
    if (!values.blobIds || values.blobIds.length === 0) {
      message.warning('请先上传本次拨付凭证,凭证将随财务报销单送交财务审批');
      return;
    }
    modalApi.lock();
@@ -159,15 +146,16 @@
      amount: values.amount,
      payDate: values.payDate,
      payee: values.payee,
      status: values.status,
      remark: values.remark,
      blobIds: values.blobIds ?? [],
      blobIds: values.blobIds,
    } as ResearchFundRecordApi.FundRecord;
    try {
      await (data.id ? updateFundRecord(data) : createFundRecord(data));
      await modalApi.close();
      emit('success');
      message.success('保存成功');
      message.success(
        data.id ? '已重新发起财务报销审批' : '登记成功,已自动提交财务报销审批',
      );
    } finally {
      modalApi.unlock();
    }
@@ -176,6 +164,7 @@
    if (!isOpen) {
      isEdit.value = false;
      remainder.value = undefined;
      rejectReason.value = '';
      return;
    }
    const data = modalApi.getData<{
@@ -185,6 +174,10 @@
    }>();
    isEdit.value = !!data.record;
    remainder.value = data.remainder;
    rejectReason.value =
      data.record?.status === FundStatus.REJECTED
        ? data.record.rejectReason || '财务未填写驳回原因'
        : '';
    await formApi.resetForm();
    if (data.record) {
      const blobIds = (data.record.attachmentList ?? []).map((item) => ({
@@ -206,7 +199,6 @@
      await formApi.setValues(
        {
          applicationId: data.applicationId,
          status: FundStatus.PENDING,
        },
        false,
      );
@@ -217,11 +209,18 @@
<template>
  <Modal
    :title="isEdit ? '编辑拨款记录' : '登记拨款记录'"
    :title="isEdit ? '修改并重新发起报销' : '登记拨款(登记后自动提交财务报销审批)'"
    class="w-1/2"
    :close-on-click-modal="false"
  >
    <Alert
      v-if="rejectReason"
      class="mx-3 mb-3"
      type="error"
      show-icon
      :message="`财务报销已驳回:${rejectReason}`"
    />
    <Alert
      v-if="alertMessage"
      class="mx-3 mb-3"
      type="info"