问题分析: 1. 后端Controller路径不一致: - fund-cust: /api/v1/customer (不是/api/v1/cust/customer) - fund-proj: /api/v1/project (不是/api/v1/proj/project) - fund-sys: /api/v1/auth 和 /api/v1/sys/* 两种路径 2. 之前的错误修改导致路径不匹配 解决方案: - 网关: StripPrefix=1 + PrefixPath=/api/v1 - 前端: baseURL=/fund,路径直接对应后端路径 网关路由配置: - fund-sys: /fund/auth/**,/fund/sys/** -> /api/v1/auth/*,/api/v1/sys/* - fund-cust: /fund/customer/** -> /api/v1/customer/* - fund-proj: /fund/project/**,/fund/requirement/** -> /api/v1/project/*,/api/v1/requirement/* - fund-exp: /fund/exp/** -> /api/v1/exp/* - fund-receipt: /fund/receipt/** -> /api/v1/receipt/* - fund-report: /fund/report/** -> /api/v1/report/* - fund-file: /fund/file/** -> /api/v1/file/* 前端API路径规范: - 认证: /auth/login -> /api/v1/auth/login - 用户: /sys/user/page -> /api/v1/sys/user/page - 客户: /customer/page -> /api/v1/customer/page - 项目: /project/page -> /api/v1/project/page - 支出: /exp/expense/page -> /api/v1/exp/expense/page - 收款: /receipt/receivable/page -> /api/v1/receipt/receivable/page - 报表: /report/stats -> /api/v1/report/stats - 文件: /file/upload -> /api/v1/file/upload 修改文件: - fund-gateway/application.yml: 路由配置调整 - TenantGatewayFilter.java: 白名单路径修正 - TokenAuthFilter.java: 白名单路径修正 - fund-admin/src/api/*.ts: 所有API路径修正 - fund-mobile/src/api/index.ts: 所有API路径修正 - FileUpload组件: 上传路径修正
103 lines
2.8 KiB
TypeScript
103 lines
2.8 KiB
TypeScript
import request from './request'
|
|
|
|
// 报表统计API
|
|
|
|
export interface DashboardStats {
|
|
unpaidReceivable: number
|
|
pendingExpense: number
|
|
todayIncome: number
|
|
todayExpense: number
|
|
pendingApprovalCount: number
|
|
overdueReceivableCount: number
|
|
}
|
|
|
|
export interface TrendItem {
|
|
date: string
|
|
income: number
|
|
expense: number
|
|
}
|
|
|
|
export interface DistributionItem {
|
|
name: string
|
|
count: number
|
|
value: number | string
|
|
}
|
|
|
|
// 项目收支分析DTO
|
|
export interface ProjectFinance {
|
|
projectId: number
|
|
projectCode: string
|
|
projectName: string
|
|
customerName: string
|
|
status: string
|
|
receivableAmount: number
|
|
receivedAmount: number
|
|
unreceivedAmount: number
|
|
receiveRate: number
|
|
expenseAmount: number
|
|
profit: number
|
|
profitRate: number
|
|
}
|
|
|
|
// 获取仪表盘统计数据
|
|
export function getDashboardStats() {
|
|
return request.get<DashboardStats>('/report/dashboard/stats')
|
|
}
|
|
|
|
// 获取收支趋势
|
|
export function getTrend(period: 'week' | 'month' | 'quarter' = 'week') {
|
|
return request.get<TrendItem[]>('/report/trend', { params: { period } })
|
|
}
|
|
|
|
// 获取项目状态分布
|
|
export function getProjectStatusDistribution() {
|
|
return request.get<DistributionItem[]>('/report/project/status-distribution')
|
|
}
|
|
|
|
// 获取支出类型分布
|
|
export function getExpenseTypeDistribution() {
|
|
return request.get<DistributionItem[]>('/report/expense/type-distribution')
|
|
}
|
|
|
|
// 获取项目收支分析
|
|
export function getProjectFinance(params?: { status?: string; customerId?: number }) {
|
|
return request.get<ProjectFinance[]>('/report/project/finance', { params })
|
|
}
|
|
|
|
// 导出项目收支分析Excel
|
|
export function exportProjectFinance(params?: { status?: string; customerId?: number }) {
|
|
const baseUrl = import.meta.env.VITE_API_URL || ''
|
|
const token = localStorage.getItem('token')
|
|
const tenantId = localStorage.getItem('tenantId') || '1'
|
|
|
|
// 构建查询参数
|
|
const queryParams = new URLSearchParams()
|
|
if (params?.status) queryParams.append('status', params.status)
|
|
if (params?.customerId) queryParams.append('customerId', String(params.customerId))
|
|
|
|
const queryString = queryParams.toString()
|
|
const url = `${baseUrl}/report/project/finance/export${queryString ? '?' + queryString : ''}`
|
|
|
|
// 使用fetch下载
|
|
return fetch(url, {
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
'X-Tenant-Id': tenantId
|
|
}
|
|
}).then(response => {
|
|
if (!response.ok) throw new Error('导出失败')
|
|
|
|
// 获取文件名
|
|
const contentDisposition = response.headers.get('Content-Disposition')
|
|
let filename = '项目收支分析报表.xlsx'
|
|
if (contentDisposition) {
|
|
const match = contentDisposition.match(/filename\*=utf-8''(.+)/i)
|
|
if (match && match[1]) {
|
|
filename = decodeURIComponent(match[1])
|
|
}
|
|
}
|
|
|
|
return response.blob().then(blob => ({ blob, filename }))
|
|
})
|
|
}
|