vault.ts 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import { ApiCode } from '@/config'
  2. import type { ApiResponse } from '@/types/api'
  3. import type { VaultListItem, VaultTransactionItem } from '@/types/vault'
  4. import { http, postJson } from './http'
  5. export function fetchVaultsList() {
  6. return postJson<VaultListItem[]>('/vaultody/vaults/list', {})
  7. }
  8. export interface FetchVaultTransactionsBody {
  9. vaultId?: string | number | null
  10. /** 页码,从 1 开始 */
  11. page?: number
  12. pageSize?: number
  13. }
  14. export interface VaultTransactionsResult {
  15. list: VaultTransactionItem[]
  16. /** 总条数,用于分页;若后端暂未返回可先不传,前端会按当前页条数展示 */
  17. total?: number
  18. }
  19. function buildVaultTransactionsPayload(body: FetchVaultTransactionsBody): Record<string, unknown> {
  20. const payload: Record<string, unknown> = {}
  21. if (body.vaultId != null) payload.vaultId = body.vaultId
  22. if (body.page != null) payload.page = body.page
  23. if (body.pageSize != null) payload.pageSize = body.pageSize
  24. return payload
  25. }
  26. export function fetchVaultTransactions(body: FetchVaultTransactionsBody = {}) {
  27. return postJson<VaultTransactionsResult>('/vaultody/vaults/transactions', buildVaultTransactionsPayload(body))
  28. }
  29. function parseFilenameFromContentDisposition(cd: string | undefined): string | null {
  30. if (!cd) return null
  31. const m = /filename\*=UTF-8''([^;\n]+)|filename="?([^";\n]+)"?/i.exec(cd)
  32. const raw = m?.[1] ?? m?.[2]
  33. if (!raw) return null
  34. try {
  35. return decodeURIComponent(raw.trim())
  36. } catch {
  37. return raw.trim()
  38. }
  39. }
  40. async function tryParseJsonErrorBlob(blob: Blob): Promise<string | null> {
  41. const text = await blob.text()
  42. try {
  43. const json = JSON.parse(text) as ApiResponse
  44. if (typeof json.code === 'number' && json.code !== ApiCode.StatusOK) {
  45. return json.msg || 'Export failed'
  46. }
  47. } catch {
  48. /* not JSON */
  49. }
  50. return null
  51. }
  52. /** POST /vaultody/export,请求体与列表接口一致;成功返回文件 blob */
  53. export async function exportVaultTransactions(
  54. body: FetchVaultTransactionsBody = {}
  55. ): Promise<{ blob: Blob; filename: string | null } | { error: string }> {
  56. const res = await http.post('/vaultody/export', buildVaultTransactionsPayload(body), {
  57. responseType: 'blob',
  58. })
  59. const raw = res.data
  60. if (!(raw instanceof Blob)) {
  61. const api = raw as ApiResponse
  62. return { error: api?.msg ?? 'Export failed' }
  63. }
  64. const blob = raw
  65. const ct = String(res.headers['content-type'] ?? res.headers['Content-Type'] ?? '').toLowerCase()
  66. if (ct.includes('application/json') || ct.includes('text/json')) {
  67. const err = await tryParseJsonErrorBlob(blob)
  68. return { error: err ?? 'Export failed' }
  69. }
  70. const filename = parseFilenameFromContentDisposition(
  71. res.headers['content-disposition'] ?? res.headers['Content-Disposition']
  72. )
  73. return { blob, filename }
  74. }