From 07aab8bb335132d5dc56f2b000301457079e8539 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 29 九月 2026 10:49:01 +0800
Subject: [PATCH] feat(research): 完善经费拨付流程支持财务报销审批
---
src/views/research/application/data.ts | 16 +++++++
src/api/research/fund/index.ts | 10 +++-
src/views/research/application/modules/fund-form.vue | 49 ++++++++++++------------
src/views/research/application/modules/fund-panel.vue | 39 ++++++++++++++++---
4 files changed, 78 insertions(+), 36 deletions(-)
diff --git a/src/api/research/fund/index.ts b/src/api/research/fund/index.ts
index 3e7ceab..e0c949d 100644
--- a/src/api/research/fund/index.ts
+++ b/src/api/research/fund/index.ts
@@ -11,7 +11,11 @@
amount: number; // 鎷ㄤ粯閲戦锛堝厓锛�
payDate?: string; // 鎷ㄤ粯鏃ユ湡
payee?: string; // 鏀舵鍗曚綅/璐︽埛鍚�
- status?: number; // 鎷ㄤ粯鐘舵�侊紝瀛楀吀 research_fund_status
+ status?: number; // 鎷ㄤ粯鐘舵�侊紝瀛楀吀 research_fund_status锛�0-寰呮嫧浠� 15-鎶ラ攢瀹℃壒涓� 20-宸叉嫧浠� 30-宸查┏鍥烇級
+ reimbursementId?: number; // 鍏宠仈璐㈠姟鎶ラ攢鍗� ID
+ reimbursementNo?: string; // 鍏宠仈璐㈠姟鎶ラ攢鍗曞彿
+ rejectReason?: string; // 璐㈠姟鎶ラ攢椹冲洖鍘熷洜锛圔PM 鍥炲啓锛�
+ rejectTime?: string; // 璐㈠姟鎶ラ攢椹冲洖鏃堕棿
remark?: string; // 澶囨敞
creator?: string; // 鍒涘缓浜� ID
creatorName?: string; // 鍒涘缓浜哄鍚�
@@ -30,12 +34,12 @@
);
}
-/** 鏂板缁忚垂鎷ㄤ粯璁板綍 */
+/** 鐧昏缁忚垂鎷ㄤ粯璁板綍锛堢櫥璁板悗鑷姩鐢熸垚骞舵彁浜よ储鍔℃姤閿�瀹℃壒锛� */
export function createFundRecord(data: ResearchFundRecordApi.FundRecord) {
return requestClient.post('/research/fund-record/create', data);
}
-/** 淇敼缁忚垂鎷ㄤ粯璁板綍锛堜粎寰呮嫧浠樺彲鏀癸級 */
+/** 淇敼骞堕噸鏂板彂璧锋姤閿�锛堜粎銆屽緟鎷ㄤ粯銆嶃�屽凡椹冲洖銆嶅彲鏀癸級 */
export function updateFundRecord(data: ResearchFundRecordApi.FundRecord) {
return requestClient.put('/research/fund-record/update', data);
}
diff --git a/src/views/research/application/data.ts b/src/views/research/application/data.ts
index b427dd3..258cb62 100644
--- a/src/views/research/application/data.ts
+++ b/src/views/research/application/data.ts
@@ -28,7 +28,9 @@
/** 缁忚垂鎷ㄤ粯鐘舵�侊紙瀵瑰簲瀛楀吀 research_fund_status锛� */
export const FundStatus = {
PENDING: 0, // 寰呮嫧浠�
+ APPROVING: 15, // 鎶ラ攢瀹℃壒涓�
PAID: 20, // 宸叉嫧浠�
+ REJECTED: 30, // 宸查┏鍥�
} as const;
/** 璇鹃鐢虫姤涔﹁〃鍗曢厤缃� */
@@ -516,13 +518,25 @@
{
field: 'status',
title: '鎷ㄤ粯鐘舵��',
- minWidth: 100,
+ minWidth: 110,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.RESEARCH_FUND_STATUS },
},
},
{
+ field: 'reimbursementNo',
+ title: '璐㈠姟鎶ラ攢鍗曞彿',
+ minWidth: 160,
+ formatter: dashIfEmpty,
+ },
+ {
+ field: 'rejectReason',
+ title: '椹冲洖鍘熷洜',
+ minWidth: 200,
+ formatter: dashIfEmpty,
+ },
+ {
field: 'hasAttachment',
title: '鎷ㄤ粯鍑瘉',
minWidth: 100,
diff --git a/src/views/research/application/modules/fund-form.vue b/src/views/research/application/modules/fund-form.vue
index 33b2be5..107af50 100644
--- a/src/views/research/application/modules/fund-form.vue
+++ b/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"
diff --git a/src/views/research/application/modules/fund-panel.vue b/src/views/research/application/modules/fund-panel.vue
index f3900fe..49fef57 100644
--- a/src/views/research/application/modules/fund-panel.vue
+++ b/src/views/research/application/modules/fund-panel.vue
@@ -39,6 +39,10 @@
() => (Number(application.value?.budgetAmount) || 0) - registeredAmount.value,
);
+/** 鍚勭姸鎬佽褰曟暟閲忥紝鐢ㄤ簬椤堕儴鎻愮ず锛�0 鏉℃椂涓嶆彁绀猴級 */
+const statusCount = (status: number) =>
+ records.value.filter((item) => item.status === status).length;
+
const [FundFormModal, fundFormModalApi] = useVbenModal({
connectedComponent: FundForm,
destroyOnClose: true,
@@ -176,6 +180,25 @@
</div>
</div>
+ <Alert
+ v-if="statusCount(FundStatus.APPROVING) > 0"
+ type="info"
+ show-icon
+ :message="`鏈� ${statusCount(FundStatus.APPROVING)} 鏉℃嫧浠樿褰曟鍦ㄦ姤閿�瀹℃壒涓紝鏆備笉鍙慨鏀癸紱璐㈠姟瀹℃壒缁撴灉灏嗚嚜鍔ㄥ洖鍐欐湰璁板綍`"
+ />
+ <Alert
+ v-if="statusCount(FundStatus.PAID) > 0"
+ type="success"
+ show-icon
+ :message="`鏈� ${statusCount(FundStatus.PAID)} 鏉℃嫧浠樿褰曡储鍔″凡閫氳繃锛屾嫧娆惧畬鎴恅"
+ />
+ <Alert
+ v-if="statusCount(FundStatus.REJECTED) > 0"
+ type="error"
+ show-icon
+ :message="`鏈� ${statusCount(FundStatus.REJECTED)} 鏉℃嫧浠樿褰曡储鍔″凡椹冲洖锛岃淇敼閲戦鎴栧嚟璇佸悗鐐广�岄噸鏂板彂璧锋姤閿�銆峘"
+ />
+
<div v-if="editable">
<TableAction
:actions="[
@@ -193,13 +216,7 @@
<Grid>
<template #hasAttachment="{ row }">
<Tag :color="row.hasAttachment ? 'success' : 'warning'">
- {{
- row.hasAttachment
- ? '宸蹭笂浼�'
- : row.status === FundStatus.PAID
- ? '缂哄け'
- : '涓嶉渶瑕�'
- }}
+ {{ row.hasAttachment ? '宸蹭笂浼�' : '缂哄け' }}
</Tag>
</template>
<template #actions="{ row }">
@@ -215,6 +232,14 @@
onClick: handleEdit.bind(null, row),
},
{
+ label: '閲嶆柊鍙戣捣鎶ラ攢',
+ type: 'link',
+ icon: ACTION_ICON.REFRESH,
+ auth: ['research:fund:update'],
+ ifShow: () => row.status === FundStatus.REJECTED,
+ onClick: handleEdit.bind(null, row),
+ },
+ {
label: '鍒犻櫎',
type: 'link',
danger: true,
--
Gitblit v1.9.3