import type { PageResult } from '@/types/api' import type { Bill, BillQuery, BillVo, FinanceStats, Invoice, InvoiceQuery, InvoiceTitle, InvoiceTitlePayload, PayMethod, PayOrderResponse, Payment, PaymentResult, } from '@/types/business/finance' import { toBills, toPlanBills, type PlanLike } from '@/utils/billVo' import { toPayMethod } from '@/utils/payMeta' import { request } from '@/utils/request' 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', }, ] 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', }, ] const MOCK_TITLES: InvoiceTitle[] = [ { id: 'title-001', type: '企业单位', title: '山东日照有限有限公司', taxNo: '344683434513543543545535', isDefault: true, }, { id: 'title-002', type: '企业单位', title: '山东省一周练七次有限公司', taxNo: '6856754615216565476846', isDefault: false, }, ] const MOCK_PAYMENTS = new Map() const MOCK_RESULTS = new Map() 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' }, ], } export function getFinanceStats(period?: string): Promise { void period return Promise.resolve(structuredClone(DEFAULT_STATS)) } function isRealBillId(id: string): boolean { return /^\d+$/.test(id) } async function fetchOwnBills(): Promise { const rows = await request({ url: '/web/bill/list', method: 'get' }) return toBills(rows) } 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 async function getBillList(query: BillQuery): Promise> { const all = await fetchOwnBills() const filtered = all.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 { list: filtered.slice(start, start + query.pageSize), total: filtered.length } } export async function getBillDetail(id: string): Promise { const rows = await request({ url: '/web/bill/list', method: 'get', params: { ids: id } }) return toBills(rows)[0] ?? null } // ═══════════════════════════════════════════════════════════════════════ // --------------------------------------------------------------------- // ═══════════════════════════════════════════════════════════════════════ export function createPayment(billId: string, method: PayMethod): Promise { 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 { return createPayment(billId, method) } export async function getPayResult(billId: string): Promise { if (isRealBillId(billId)) { const bill = await getBillDetail(billId) if (!bill || bill.status !== '已支付') return null return { method: toPayMethod(bill.payChannel), amount: bill.amount, payTime: bill.payTime ?? '—', tradeNo: bill.tradeNo ?? '—', bills: [bill], } } return getLegacyPayResult(billId) } function getLegacyPayResult(billId: string): Promise { 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) 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> { 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 { 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 { return Promise.resolve(MOCK_TITLES.map((item) => ({ ...item }))) } export function createInvoiceTitle(payload: InvoiceTitlePayload): Promise { 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 { 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 { 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 { for (const item of MOCK_TITLES) { item.isDefault = item.id === id } return Promise.resolve(MOCK_TITLES.map((item) => ({ ...item }))) } export function exportReport(): Promise { return Promise.resolve() } // ═══════════════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════════════ export async function getCheckoutBills(requirementId: string, ids: string[] = []): Promise { if (!/^\d+$/.test(requirementId)) return [] const vo = await request<{ billPlans?: PlanLike[] }>({ url: `/web/requirementPool/get/finance/${requirementId}`, method: 'get', }) const all = toPlanBills(vo?.billPlans) return ids.length > 0 ? all.filter((bill) => ids.includes(bill.id)) : all.filter((bill) => bill.status === '待支付') } export async function getPlanPayResult(requirementId: string, planId: string): Promise { const bills = await getCheckoutBills(requirementId, [planId]) const bill = bills[0] if (!bill || bill.status !== '已支付') return null return { method: toPayMethod(bill.payChannel), amount: bill.amount, payTime: bill.payTime ?? '—', tradeNo: bill.tradeNo ?? '—', bills: [bill], } } export function createWechatNativeOrder( orderNo: string, amount: number, billIds: string[] = [], ): Promise { return request({ url: '/pay/web/wechat/native', method: 'post', data: { orderNo, amount, billIds }, }) } export function createAlipayPageOrder( outTradeNo: string, amount: number, billIds: string[] = [], ): Promise { return request({ url: '/pay/web/alipay/page', method: 'post', data: { outTradeNo, amount, billIds }, }) }