feat(统计报表): 添加年度统计报表功能并显示现金合计
Some checks failed
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled

- 将下载全部报表改为下载年度统计报表
- 在类型定义和数据处理中添加 cashFee 字段
- 在报表页面显示现金合计金额
- 添加下载年度统计报表和下载不含天医币报表的接口和功能
- 使用年份选择器替代原有下载按钮
This commit is contained in:
2026-04-01 14:38:32 +08:00
parent d7ea77a5db
commit 09b194ba35
6 changed files with 105 additions and 17 deletions

View File

@@ -144,9 +144,33 @@ export const statisticsApi = {
* 下载全部月份的收入统计报表
* @returns 全部月份的收入统计报表数据
*/
downloadAllMonthIncomeStatistics: () => {
downloadAllMonthIncomeStatistics: (data?: { year: number | string }) => {
return defaultRequestClient.download<Blob>('common/statistics/getMonthStatistics', {
data: {},
data: data || {},
});
},
/**
* 下载年度统计报表
* @param data 请求参数
* @param data.year 年份
* @returns 年度统计报表数据
*/
downloadYearStatistics: (data: { year: number | string }) => {
return defaultRequestClient.download<Blob>('common/statistics/getYearStatistics', {
data,
});
},
/**
* 下载年度统计报表(不包含天医币)
* @param data 请求参数
* @param data.year 年份
* @returns 年度统计报表数据(不包含天医币)
*/
downloadYearStatisticsNoPoint: (data: { year: number | string }) => {
return defaultRequestClient.download<Blob>('common/statistics/getYearStatisticsNoPoint', {
data,
});
},
};

View File

@@ -67,7 +67,7 @@ const routes: RouteRecordRaw[] = [
},
{
meta: {
title: '下载全部报表',
title: '下载年度报表',
keepAlive: true,
},
name: 'DownloadReports',

View File

@@ -1,39 +1,86 @@
<script lang="ts" setup>
import type { Dayjs } from 'dayjs';
import { ref } from 'vue';
import { Page } from '@vben/common-ui';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Button } from 'ant-design-vue';
import { Button, DatePicker } from 'ant-design-vue';
import dayjs from 'dayjs';
import { statisticsApi } from '#/api/statistics';
// 下载全部报表
const downloadAllReportsLoading = ref(false);
async function downloadAllReports() {
downloadAllReportsLoading.value = true;
const year = ref<Dayjs>(dayjs());
const downloadYearReportLoading = ref(false);
const downloadYearReportNoPointLoading = ref(false);
const disabledDate = (date: Dayjs) => date.year() > dayjs().year();
async function downloadYearReport() {
if (!year.value) return;
downloadYearReportLoading.value = true;
try {
// 下载全部报表
const Blob = await statisticsApi.downloadAllMonthIncomeStatistics();
const selectedYear = year.value.year();
const Blob = await statisticsApi.downloadYearStatistics({
year: selectedYear,
});
downloadFileFromBlobPart({
source: Blob,
fileName: '财务报表.xlsx',
fileName: `${selectedYear}年度统计报表.xlsx`,
});
} catch (error) {
console.error('下载全部报表失败:', error);
console.error('下载年度统计报表失败:', error);
} finally {
downloadAllReportsLoading.value = false;
downloadYearReportLoading.value = false;
}
}
async function downloadYearReportNoPoint() {
if (!year.value) return;
downloadYearReportNoPointLoading.value = true;
try {
const selectedYear = year.value.year();
const Blob = await statisticsApi.downloadYearStatisticsNoPoint({
year: selectedYear,
});
downloadFileFromBlobPart({
source: Blob,
fileName: `${selectedYear}年度统计报表_不含天医币.xlsx`,
});
} catch (error) {
console.error('下载年度统计报表(不包含天医币)失败:', error);
} finally {
downloadYearReportNoPointLoading.value = false;
}
}
</script>
<template>
<Page auto-content-height>
<div class="flex h-full rounded-md bg-white p-4">
<Button type="primary" :loading="downloadAllReportsLoading" @click="downloadAllReports">
下载报表
</Button>
<div class="flex h-full flex-col rounded-md bg-white">
<div class="flex items-center p-4">
<DatePicker v-model:value="year" picker="year" :disabled-date="disabledDate" />
<Button
type="primary"
class="ml-2"
:loading="downloadYearReportLoading"
@click="downloadYearReport"
>
下载年度统计报表
</Button>
<Button
type="primary"
class="ml-2"
:loading="downloadYearReportNoPointLoading"
@click="downloadYearReportNoPoint"
>
下载年度统计报表不包含天医币
</Button>
</div>
</div>
</Page>
</template>

View File

@@ -21,6 +21,7 @@ export function handleSumAndNoNeedAmortizateMonthData(
resultData.push({
type,
sumFee: resItem.sumFee || 0,
cashFee: resItem.cashFee || 0,
children: childDictArr.map((childType) => ({
title: childType.title,
fee: resItem[childType.type] || 0,
@@ -86,6 +87,13 @@ export async function handleVipAndCourseMonthData(
.reduce((acc: number, cur: IncomeItem) => acc + cur.fee, 0)
.toFixed(3),
),
// 现金合计需要去掉天医币后合计
cashFee: Number.parseFloat(
currentMonthData.incomes
.filter((item: IncomeItem) => item.type !== '天医币')
.reduce((acc: number, cur: IncomeItem) => acc + cur.fee, 0)
.toFixed(3),
),
};
}

View File

@@ -138,6 +138,7 @@ const vipMonthLoading = ref<boolean | string>(loadingText);
const vipMonthData = ref({
incomes: [] as IncomeItem[],
incomesTotal: 0,
cashFee: 0,
notyet: 0,
already: 0,
now: 0,
@@ -168,6 +169,7 @@ const courseMonthLoading = ref<boolean | string>(loadingText);
const courseMonthData = ref({
incomes: [] as IncomeItem[],
incomesTotal: 0,
cashFee: 0,
notyet: 0,
already: 0,
now: 0,
@@ -403,6 +405,9 @@ async function downloadMonthStatistics() {
<div class="flex items-center">
{{ `合计:${item.sumFee}` }}
</div>
<div class="flex items-center">
{{ `现金合计:${item.cashFee}` }}
</div>
</div>
</div>
</div>
@@ -427,6 +432,7 @@ async function downloadMonthStatistics() {
<div v-else>
<div class="font-bold">上月剩余摊销 {{ vipMonthData.lastNotyet }}</div>
<div class="font-bold">收入 合计{{ vipMonthData.incomesTotal }}</div>
<div class="font-bold">现金合计{{ vipMonthData.cashFee }}</div>
<div
v-for="child in vipMonthData.incomes"
:key="child.type"
@@ -458,6 +464,7 @@ async function downloadMonthStatistics() {
<div v-else>
<div class="font-bold">上月剩余摊销 {{ courseMonthData.lastNotyet }}</div>
<div class="font-bold">收入 合计{{ courseMonthData.incomesTotal }}</div>
<div class="font-bold">现金合计{{ courseMonthData.cashFee }}</div>
<div
v-for="child in courseMonthData.incomes"
:key="child.type"

View File

@@ -4,12 +4,14 @@ export interface NoNeedAmortizateMonthStatistics {
zfb: number;
tianyibi: number;
sumFee: number;
cashFee: number;
type: string;
}
export interface SumAndNoNeedAmortizateMonthDataItem {
type: string;
sumFee: number;
cashFee: number;
children: Array<{
fee: number;
title: string;