feat 搭建架构骨架
This commit is contained in:
316
src/api/finance.ts
Normal file
316
src/api/finance.ts
Normal file
@@ -0,0 +1,316 @@
|
||||
import type { PageResult } from '@/types/api'
|
||||
import type {
|
||||
Bill,
|
||||
BillQuery,
|
||||
FinanceStats,
|
||||
Invoice,
|
||||
InvoiceQuery,
|
||||
InvoiceTitle,
|
||||
InvoiceTitlePayload,
|
||||
PayMethod,
|
||||
Payment,
|
||||
PaymentResult,
|
||||
} from '@/types/business/finance'
|
||||
|
||||
// TODO(api): 待接口文档到位后,将以下 mock 替换为 request<T> 调用
|
||||
|
||||
/** 账单 mock(可变:支付后状态在前端内存中翻转演示) */
|
||||
const MOCK_BILLS: Bill[] = [
|
||||
{
|
||||
id: 'bill-001',
|
||||
planName: '第一期·预付款 比例33%',
|
||||
billNo: '20260813ZZ251245',
|
||||
tradeNo: 'T20260819153035678K2M001',
|
||||
projectName: '某大型银行数字化转型方案',
|
||||
amount: 20000,
|
||||
status: '已支付',
|
||||
createTime: '2026-8-17 14:00',
|
||||
},
|
||||
{
|
||||
id: 'bill-002',
|
||||
planName: '第二期·进度款 比例30%',
|
||||
billNo: '20260813ZZ251246',
|
||||
projectName: '智慧城市大数据平台建设项目',
|
||||
amount: 20100,
|
||||
status: '待支付',
|
||||
createTime: '2026-8-17 14:00',
|
||||
},
|
||||
{
|
||||
id: 'bill-003',
|
||||
planName: '第三期·验收款 比例27%',
|
||||
billNo: '20260813ZZ251247',
|
||||
projectName: '三甲医院指挥医疗信息化系统',
|
||||
amount: 20000,
|
||||
status: '未到期',
|
||||
createTime: '2026-8-17 14:00',
|
||||
},
|
||||
{
|
||||
id: 'bill-004',
|
||||
planName: '第四期·尾款 比例10%',
|
||||
billNo: '20260813ZZ251248',
|
||||
projectName: '智慧城市大数据平台建设项目',
|
||||
amount: 20100,
|
||||
status: '未到期',
|
||||
createTime: '2026-8-17 14:00',
|
||||
},
|
||||
{
|
||||
id: 'bill-005',
|
||||
planName: '第二期·进度款 比例30%',
|
||||
billNo: '20260813ZZ251249',
|
||||
projectName: '三甲医院指挥医疗信息化系统',
|
||||
amount: 20100,
|
||||
status: '待支付',
|
||||
createTime: '2026-8-16 10:30',
|
||||
},
|
||||
{
|
||||
id: 'bill-006',
|
||||
planName: '第一期·预付款 比例33%',
|
||||
billNo: '20260813ZZ251250',
|
||||
tradeNo: 'T20260819153035678K2M002',
|
||||
projectName: '某大型银行数字化转型方案',
|
||||
amount: 20000,
|
||||
status: '已支付',
|
||||
createTime: '2026-8-15 09:12',
|
||||
},
|
||||
]
|
||||
|
||||
/** 发票 mock(可变:申请开票后状态在前端内存中翻转演示) */
|
||||
const MOCK_INVOICES: Invoice[] = [
|
||||
{
|
||||
id: 'invoice-001',
|
||||
planName: '第一期·预付款 比例33%',
|
||||
billNo: '20260813ZZ251245',
|
||||
tradeNo: 'T20260819153035678K2M001',
|
||||
projectName: '某大型银行数字化转型方案',
|
||||
amount: 20000,
|
||||
status: '已开票',
|
||||
applyTime: '2026-8-17 14:00',
|
||||
},
|
||||
{
|
||||
id: 'invoice-002',
|
||||
planName: '第一期·预付款 比例33%',
|
||||
billNo: '20260813ZZ251250',
|
||||
tradeNo: 'T20260819153035678K2M002',
|
||||
projectName: '某大型银行数字化转型方案',
|
||||
amount: 20000,
|
||||
status: '开票中',
|
||||
applyTime: '2026-8-17 14:00',
|
||||
},
|
||||
{
|
||||
id: 'invoice-003',
|
||||
planName: '第二期·进度款 比例30%',
|
||||
billNo: '20260813ZZ251249',
|
||||
projectName: '三甲医院指挥医疗信息化系统',
|
||||
amount: 20100,
|
||||
status: '未开票',
|
||||
applyTime: '2026-8-17 14:00',
|
||||
},
|
||||
]
|
||||
|
||||
/** 发票抬头 mock(可变,CRUD 在前端内存中演示) */
|
||||
const MOCK_TITLES: InvoiceTitle[] = [
|
||||
{
|
||||
id: 'title-001',
|
||||
type: '企业单位',
|
||||
title: '山东日照有限有限公司',
|
||||
taxNo: '344683434513543543545535',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
id: 'title-002',
|
||||
type: '企业单位',
|
||||
title: '山东省一周练七次有限公司',
|
||||
taxNo: '6856754615216565476846',
|
||||
isDefault: false,
|
||||
},
|
||||
]
|
||||
|
||||
/** 支付进行单(billId → 当前二维码) */
|
||||
const MOCK_PAYMENTS = new Map<string, Payment>()
|
||||
|
||||
/** 已产生支付结果的账单(billId → 结果),预置已支付账单 */
|
||||
const MOCK_RESULTS = new Map<string, PaymentResult>()
|
||||
MOCK_RESULTS.set('bill-001', {
|
||||
method: 'wechat',
|
||||
amount: 20000,
|
||||
payTime: '2026-01-21 15:00:00',
|
||||
tradeNo: 'T20260819153035678K2M001',
|
||||
bills: [{ ...MOCK_BILLS[0] }],
|
||||
})
|
||||
MOCK_RESULTS.set('bill-006', {
|
||||
method: 'wechat',
|
||||
amount: 20000,
|
||||
payTime: '2026-01-21 15:00:00',
|
||||
tradeNo: 'T20260819153035678K2M002',
|
||||
bills: [{ ...MOCK_BILLS[5] }],
|
||||
})
|
||||
|
||||
const DEFAULT_STATS: FinanceStats = {
|
||||
totalExpense: 1726823900,
|
||||
month: '2026年1',
|
||||
pendingCount: 3,
|
||||
distribution: [
|
||||
{ name: '生产智造', value: 25, color: '#3370ff' },
|
||||
{ name: '物联网全案', value: 25, color: '#34c724' },
|
||||
{ name: '硬件电路', value: 25, color: '#ff8f1f' },
|
||||
{ name: '互联网软件', value: 25, color: '#f54a45' },
|
||||
],
|
||||
}
|
||||
|
||||
/** 财务统计(period 为展示周期参数,mock 恒返回原型数据) */
|
||||
export function getFinanceStats(period?: string): Promise<FinanceStats> {
|
||||
void period
|
||||
return Promise.resolve(structuredClone(DEFAULT_STATS))
|
||||
}
|
||||
|
||||
function matchBillKeyword(item: Bill, keyword: string): boolean {
|
||||
if (!keyword) return true
|
||||
return (
|
||||
item.planName.includes(keyword) ||
|
||||
item.billNo.includes(keyword) ||
|
||||
item.projectName.includes(keyword) ||
|
||||
(item.tradeNo?.includes(keyword) ?? false)
|
||||
)
|
||||
}
|
||||
|
||||
/** 账单列表(筛选 + 分页) */
|
||||
export function getBillList(query: BillQuery): Promise<PageResult<Bill>> {
|
||||
const filtered = MOCK_BILLS.filter((item) => {
|
||||
if (query.status !== 'all' && item.status !== query.status) return false
|
||||
return matchBillKeyword(item, query.keyword)
|
||||
})
|
||||
const start = (query.page - 1) * query.pageSize
|
||||
return Promise.resolve({
|
||||
list: filtered.slice(start, start + query.pageSize).map((item) => ({ ...item })),
|
||||
total: filtered.length,
|
||||
})
|
||||
}
|
||||
|
||||
/** 账单详情(不存在返回 null) */
|
||||
export function getBillDetail(id: string): Promise<Bill | null> {
|
||||
const found = MOCK_BILLS.find((item) => item.id === id) ?? null
|
||||
return Promise.resolve(found ? { ...found } : null)
|
||||
}
|
||||
|
||||
/** 创建支付单(生成二维码支付信息) */
|
||||
export function createPayment(billId: string, method: PayMethod): Promise<Payment> {
|
||||
const bill = MOCK_BILLS.find((item) => item.id === billId)
|
||||
if (!bill) return Promise.reject(new Error('账单不存在'))
|
||||
const payment: Payment = {
|
||||
billId,
|
||||
method,
|
||||
amount: bill.amount,
|
||||
qrExpiresInSeconds: 300,
|
||||
}
|
||||
MOCK_PAYMENTS.set(billId, payment)
|
||||
return Promise.resolve({ ...payment })
|
||||
}
|
||||
|
||||
/** 切换支付方式(刷新二维码) */
|
||||
export function switchPaymentMethod(billId: string, method: PayMethod): Promise<Payment> {
|
||||
return createPayment(billId, method)
|
||||
}
|
||||
|
||||
/** 查询支付结果(无支付记录且未支付返回 null;演示数据 payTime/tradeNo 固定) */
|
||||
export function getPayResult(billId: string): Promise<PaymentResult | null> {
|
||||
const existing = MOCK_RESULTS.get(billId)
|
||||
if (existing) return Promise.resolve(structuredClone(existing))
|
||||
|
||||
const payment = MOCK_PAYMENTS.get(billId)
|
||||
const bill = MOCK_BILLS.find((item) => item.id === billId)
|
||||
if (!payment || !bill) return Promise.resolve(null)
|
||||
|
||||
// 演示:存在支付单即视为完成支付(createPayment 由「完成支付」触发)
|
||||
const tradeNo = bill.tradeNo ?? 'T20260819153035678K2M001'
|
||||
if (bill.status !== '已支付') {
|
||||
bill.status = '已支付'
|
||||
bill.tradeNo = tradeNo
|
||||
}
|
||||
const result: PaymentResult = {
|
||||
method: payment.method,
|
||||
amount: payment.amount,
|
||||
payTime: '2026-01-21 15:00:00',
|
||||
tradeNo,
|
||||
bills: [{ ...bill }],
|
||||
}
|
||||
MOCK_RESULTS.set(billId, result)
|
||||
return Promise.resolve(structuredClone(result))
|
||||
}
|
||||
|
||||
function matchInvoiceKeyword(item: Invoice, keyword: string): boolean {
|
||||
if (!keyword) return true
|
||||
return (
|
||||
item.planName.includes(keyword) ||
|
||||
item.billNo.includes(keyword) ||
|
||||
item.projectName.includes(keyword) ||
|
||||
(item.tradeNo?.includes(keyword) ?? false)
|
||||
)
|
||||
}
|
||||
|
||||
/** 发票列表(筛选 + 分页) */
|
||||
export function getInvoiceList(query: InvoiceQuery): Promise<PageResult<Invoice>> {
|
||||
const filtered = MOCK_INVOICES.filter((item) => {
|
||||
if (query.status !== 'all' && item.status !== query.status) return false
|
||||
return matchInvoiceKeyword(item, query.keyword)
|
||||
})
|
||||
const start = (query.page - 1) * query.pageSize
|
||||
return Promise.resolve({
|
||||
list: filtered.slice(start, start + query.pageSize).map((item) => ({ ...item })),
|
||||
total: filtered.length,
|
||||
})
|
||||
}
|
||||
|
||||
/** 申请开票(演示:状态置为开票中),返回最新发票 */
|
||||
export function applyInvoice(id: string): Promise<Invoice> {
|
||||
const found = MOCK_INVOICES.find((item) => item.id === id)
|
||||
if (!found) return Promise.reject(new Error('发票记录不存在'))
|
||||
found.status = '开票中'
|
||||
return Promise.resolve({ ...found })
|
||||
}
|
||||
|
||||
/** 发票抬头列表 */
|
||||
export function getInvoiceTitles(): Promise<InvoiceTitle[]> {
|
||||
return Promise.resolve(MOCK_TITLES.map((item) => ({ ...item })))
|
||||
}
|
||||
|
||||
/** 新增发票抬头 */
|
||||
export function createInvoiceTitle(payload: InvoiceTitlePayload): Promise<InvoiceTitle> {
|
||||
const created: InvoiceTitle = {
|
||||
id: `title-${Date.now()}`,
|
||||
...payload,
|
||||
isDefault: false,
|
||||
}
|
||||
MOCK_TITLES.push(created)
|
||||
return Promise.resolve({ ...created })
|
||||
}
|
||||
|
||||
/** 编辑发票抬头 */
|
||||
export function updateInvoiceTitle(id: string, payload: InvoiceTitlePayload): Promise<InvoiceTitle> {
|
||||
const found = MOCK_TITLES.find((item) => item.id === id)
|
||||
if (!found) return Promise.reject(new Error('抬头不存在'))
|
||||
found.type = payload.type
|
||||
found.title = payload.title
|
||||
found.taxNo = payload.taxNo
|
||||
return Promise.resolve({ ...found })
|
||||
}
|
||||
|
||||
/** 删除发票抬头 */
|
||||
export function deleteInvoiceTitle(id: string): Promise<void> {
|
||||
const index = MOCK_TITLES.findIndex((item) => item.id === id)
|
||||
if (index < 0) return Promise.reject(new Error('抬头不存在'))
|
||||
MOCK_TITLES.splice(index, 1)
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
/** 设为默认抬头(唯一默认) */
|
||||
export function setDefaultTitle(id: string): Promise<InvoiceTitle[]> {
|
||||
for (const item of MOCK_TITLES) {
|
||||
item.isDefault = item.id === id
|
||||
}
|
||||
return Promise.resolve(MOCK_TITLES.map((item) => ({ ...item })))
|
||||
}
|
||||
|
||||
/** 导出报表(演示:稍后由页面提示成功) */
|
||||
export function exportReport(): Promise<void> {
|
||||
return Promise.resolve()
|
||||
}
|
||||
Reference in New Issue
Block a user