架构说明: - 前端 baseURL: /fund - 网关路由:Path=/fund/exp/** - StripPrefix=1: 去掉/fund 前缀 - PrefixPath=/api/v1: 自动添加/api/v1 前缀 - 后端 Controller: /api/v1/exp/expense-type 请求流程: 前端:GET /fund/exp/expense-type/page 网关:匹配 Path=/fund/exp/** → StripPrefix=1 → PrefixPath=/api/v1 后端:接收 /api/v1/exp/expense-type/page ✓ 与其他模块保持一致: - customer: GET /customer/page → /api/v1/customer/page - project: GET /project/page → /api/v1/project/page - expense: GET /exp/expense-type/page → /api/v1/exp/expense-type/page 设计原则: - 前端不硬编码/api/v1,由网关统一处理 - 符合网关路由与 API路径分离的架构设计
101 lines
3.3 KiB
TypeScript
101 lines
3.3 KiB
TypeScript
import { request } from './request'
|
|
|
|
// 支出类型
|
|
export function getExpenseTypeList(params: { pageNum: number; pageSize: number; typeName?: string; status?: string }) {
|
|
return request.get('/exp/expense-type/page', { params })
|
|
}
|
|
|
|
export function getExpenseTypeTree() {
|
|
return request.get('/exp/expense-type/tree')
|
|
}
|
|
|
|
export function createExpenseType(data: any) {
|
|
return request.post('/exp/expense-type', data)
|
|
}
|
|
|
|
export function updateExpenseType(id: number, data: any) {
|
|
return request.put(`/exp/expense-type/${id}`, data)
|
|
}
|
|
|
|
export function deleteExpenseType(id: number) {
|
|
return request.delete(`/exp/expense-type/${id}`)
|
|
}
|
|
|
|
// 支出管理
|
|
export function getExpenseList(params: { pageNum: number; pageSize: number; title?: string; expenseType?: number; approvalStatus?: number; payStatus?: number }) {
|
|
return request.get('/exp/expense/page', { params })
|
|
}
|
|
|
|
export function getExpenseById(id: number) {
|
|
return request.get(`/exp/expense/${id}`)
|
|
}
|
|
|
|
export function createExpense(data: any) {
|
|
return request.post('/exp/expense', data)
|
|
}
|
|
|
|
export function updateExpense(id: number, data: any) {
|
|
return request.put(`/exp/expense/${id}`, data)
|
|
}
|
|
|
|
export function deleteExpense(id: number) {
|
|
return request.delete(`/exp/expense/${id}`)
|
|
}
|
|
|
|
// 审批流程
|
|
export function submitExpense(id: number) {
|
|
return request.post(`/exp/expense/${id}/submit`)
|
|
}
|
|
|
|
export function withdrawExpense(id: number) {
|
|
return request.post(`/exp/expense/${id}/withdraw`)
|
|
}
|
|
|
|
export function approveExpense(id: number, comment: string) {
|
|
return request.put(`/exp/expense/${id}/approve?comment=${encodeURIComponent(comment)}`)
|
|
}
|
|
|
|
export function rejectExpense(id: number, comment: string) {
|
|
return request.put(`/exp/expense/${id}/reject?comment=${encodeURIComponent(comment)}`)
|
|
}
|
|
|
|
export function confirmPayExpense(id: number, payChannel: string, payVoucher?: string) {
|
|
return request.put(`/exp/expense/${id}/confirm-pay?payChannel=${payChannel}&payVoucher=${payVoucher || ''}`)
|
|
}
|
|
|
|
// 导出支出明细
|
|
export function exportExpense(params?: { title?: string; expenseType?: number; approvalStatus?: number; payStatus?: 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?.title) queryParams.append('title', params.title)
|
|
if (params?.expenseType) queryParams.append('expenseType', String(params.expenseType))
|
|
if (params?.approvalStatus !== undefined) queryParams.append('approvalStatus', String(params.approvalStatus))
|
|
if (params?.payStatus !== undefined) queryParams.append('payStatus', String(params.payStatus))
|
|
|
|
const queryString = queryParams.toString()
|
|
const url = `${baseUrl}/exp/expense/export${queryString ? '?' + queryString : ''}`
|
|
|
|
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 }))
|
|
})
|
|
}
|