<script lang="ts" setup>
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { ResearchApplicationApi } from '#/api/research/application';
|
import type { ResearchFundRecordApi } from '#/api/research/fund';
|
import type { DescriptionItemSchema } from '#/components/description';
|
|
import { h, onMounted, ref } from 'vue';
|
|
import { ContentWrap } from '@vben/common-ui';
|
import { DICT_TYPE } from '@vben/constants';
|
import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
|
|
import { Spin, Tag } from 'ant-design-vue';
|
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { getApplication, getTopicReviewList } from '#/api/research/application';
|
import { getFundRecordList } from '#/api/research/fund';
|
import { DictTag } from '#/components/dict-tag';
|
import { useDescription } from '#/components/description';
|
|
import {
|
FundStatus,
|
useFundColumns,
|
useReviewColumns,
|
} from '../../data';
|
|
defineOptions({ name: 'ResearchApplicationApproveDetail' });
|
|
const props = defineProps<{ id: string }>();
|
|
const loading = ref(false);
|
const formData = ref<ResearchApplicationApi.Application>();
|
const reviews = ref<ResearchApplicationApi.TopicReview[]>([]);
|
const fundRecords = ref<ResearchFundRecordApi.FundRecord[]>([]);
|
|
/** 文本字段空值占位 */
|
function textRender(val: unknown) {
|
return val === 0 || val ? String(val) : '-';
|
}
|
|
/** 金额字段空值占位 */
|
function amountRender(val: string | number | undefined) {
|
return val === null || val === undefined || val === ''
|
? '-'
|
: (erpPriceInputFormatter(val) as string);
|
}
|
|
const [Descriptions] = useDescription({
|
bordered: true,
|
column: 2,
|
schema: [
|
{ label: '申报编号', field: 'no', render: textRender },
|
{ label: '申报人姓名/单位名称', field: 'applicantName', render: textRender },
|
{ label: '课题名称', field: 'title', render: textRender },
|
{ label: '所属课题指南', field: 'guidelineTitle', render: textRender },
|
{
|
label: '课题类别',
|
field: 'category',
|
render: (val) =>
|
h(DictTag, {
|
type: DICT_TYPE.RESEARCH_GUIDELINE_CATEGORY,
|
value: val,
|
}),
|
},
|
{
|
label: '状态',
|
field: 'status',
|
render: (val) =>
|
h(DictTag, {
|
type: DICT_TYPE.RESEARCH_APPLICATION_STATUS,
|
value: val,
|
}),
|
},
|
{ label: '工作单位', field: 'workUnit', render: textRender },
|
{ label: '职称', field: 'jobTitle', render: textRender },
|
{ label: '联系电话', field: 'contactPhone', render: textRender },
|
{ label: '联系邮箱', field: 'contactEmail', render: textRender },
|
{
|
label: '申请经费(元)',
|
field: 'budgetAmount',
|
render: amountRender,
|
},
|
{
|
label: '评审加权均分',
|
field: 'reviewAvgScore',
|
render: (val) =>
|
val === null || val === undefined
|
? '未汇总'
|
: Number(val).toFixed(2),
|
},
|
{
|
label: '评审结论',
|
field: 'reviewPass',
|
render: (val) =>
|
val === null || val === undefined
|
? '未汇总'
|
: h(
|
Tag,
|
{ color: val === 1 ? 'success' : 'error' },
|
() => (val === 1 ? '推荐立项' : '不推荐立项'),
|
),
|
},
|
{ label: '立项编号', field: 'researchNo', render: textRender },
|
{
|
label: '立项时间',
|
field: 'approveTime',
|
render: (val) => (val ? (formatDateTime(val) as string) : '-'),
|
},
|
{ label: '创建人', field: 'creatorName', render: textRender },
|
{
|
label: '创建时间',
|
field: 'createTime',
|
render: (val) => (val ? (formatDateTime(val) as string) : '-'),
|
},
|
{ label: '备注', field: 'remark', render: textRender },
|
] as DescriptionItemSchema[],
|
});
|
|
const [ReviewGrid, reviewGridApi] = useVbenVxeGrid({
|
gridOptions: {
|
columns: useReviewColumns(false),
|
data: [],
|
minHeight: 200,
|
autoResize: true,
|
border: true,
|
rowConfig: {
|
keyField: 'id',
|
isHover: true,
|
},
|
pagerConfig: {
|
enabled: false,
|
},
|
toolbarConfig: {
|
enabled: false,
|
},
|
} as VxeTableGridOptions<ResearchApplicationApi.TopicReview>,
|
});
|
|
const [FundGrid, fundGridApi] = useVbenVxeGrid({
|
gridOptions: {
|
columns: useFundColumns(false),
|
data: [],
|
minHeight: 200,
|
autoResize: true,
|
border: true,
|
rowConfig: {
|
keyField: 'id',
|
isHover: true,
|
},
|
pagerConfig: {
|
enabled: false,
|
},
|
toolbarConfig: {
|
enabled: false,
|
},
|
} as VxeTableGridOptions<ResearchFundRecordApi.FundRecord>,
|
});
|
|
onMounted(async () => {
|
try {
|
loading.value = true;
|
const applicationId = Number(props.id);
|
const [application, reviewList, recordList] = await Promise.all([
|
getApplication(applicationId),
|
getTopicReviewList(applicationId),
|
getFundRecordList(applicationId),
|
]);
|
formData.value = application;
|
reviews.value = reviewList;
|
fundRecords.value = recordList;
|
reviewGridApi.setGridOptions({ data: reviewList });
|
fundGridApi.setGridOptions({ data: recordList });
|
} finally {
|
loading.value = false;
|
}
|
});
|
</script>
|
|
<template>
|
<ContentWrap class="m-2">
|
<Spin :spinning="loading" tip="加载中...">
|
<div class="space-y-4">
|
<!-- 申报书基本信息 -->
|
<div class="rounded-lg border border-border bg-background p-4">
|
<div class="mb-4 flex items-center gap-2">
|
<span class="inline-block h-4 w-1 rounded bg-primary" />
|
<span class="text-base font-semibold">课题申报书基本信息</span>
|
</div>
|
<Descriptions :data="formData" />
|
</div>
|
|
<!-- 课题组成员 -->
|
<div
|
v-if="formData?.teamMembers"
|
class="rounded-lg border border-border bg-background p-4"
|
>
|
<div class="mb-4 flex items-center gap-2">
|
<span class="inline-block h-4 w-1 rounded bg-primary" />
|
<span class="text-base font-semibold">课题组成员</span>
|
</div>
|
<pre class="whitespace-pre-wrap break-all font-sans">{{
|
formData.teamMembers
|
}}</pre>
|
</div>
|
|
<!-- 申报书正文 -->
|
<div
|
v-if="formData?.planContent"
|
class="rounded-lg border border-border bg-background p-4"
|
>
|
<div class="mb-4 flex items-center gap-2">
|
<span class="inline-block h-4 w-1 rounded bg-primary" />
|
<span class="text-base font-semibold">申报书正文</span>
|
</div>
|
<pre class="whitespace-pre-wrap break-all font-sans">{{
|
formData.planContent
|
}}</pre>
|
</div>
|
|
<!-- 专家评审明细 -->
|
<div class="rounded-lg border border-border bg-background p-4">
|
<div class="mb-4 flex items-center gap-2">
|
<span class="inline-block h-4 w-1 rounded bg-primary" />
|
<span class="text-base font-semibold">专家评审明细</span>
|
<span v-if="reviews.length" class="text-gray-500">
|
(共 {{ reviews.length }} 位专家)
|
</span>
|
</div>
|
<ReviewGrid v-if="reviews.length" />
|
<p v-else class="text-gray-500">暂无专家评审记录</p>
|
</div>
|
|
<!-- 经费拨付记录 -->
|
<div class="rounded-lg border border-border bg-background p-4">
|
<div class="mb-4 flex items-center gap-2">
|
<span class="inline-block h-4 w-1 rounded bg-primary" />
|
<span class="text-base font-semibold">经费拨付记录</span>
|
</div>
|
<FundGrid v-if="fundRecords.length">
|
<template #hasAttachment="{ row }">
|
<Tag :color="row.hasAttachment ? 'success' : 'warning'">
|
{{
|
row.hasAttachment
|
? '已上传'
|
: row.status === FundStatus.PAID
|
? '缺失'
|
: '不需要'
|
}}
|
</Tag>
|
</template>
|
</FundGrid>
|
<p v-else class="text-gray-500">暂无经费拨付记录</p>
|
</div>
|
|
<!-- 申报书附件 -->
|
<div
|
v-if="formData?.attachmentList?.length"
|
class="rounded-lg border border-border bg-background p-4"
|
>
|
<div class="mb-4 flex items-center gap-2">
|
<span class="inline-block h-4 w-1 rounded bg-primary" />
|
<span class="text-base font-semibold">申报书附件</span>
|
</div>
|
<div class="flex flex-wrap gap-3">
|
<a
|
v-for="file in formData.attachmentList"
|
:key="file.id"
|
:href="file.previewURL || file.url"
|
target="_blank"
|
class="text-blue-500 hover:underline"
|
>
|
{{ file.name || `附件-${file.id}` }}
|
</a>
|
</div>
|
</div>
|
</div>
|
</Spin>
|
</ContentWrap>
|
</template>
|