diff --git a/apps/daxpay-admin/src/api/payment/dashboard/trade-dashboard.api.ts b/apps/daxpay-admin/src/api/payment/dashboard/trade-dashboard.api.ts new file mode 100644 index 000000000..9dd44d31d --- /dev/null +++ b/apps/daxpay-admin/src/api/payment/dashboard/trade-dashboard.api.ts @@ -0,0 +1,168 @@ +import type { Result } from '#/types/web'; + +import { defHttp } from '#/api/request'; + +/** + * 工作台/分析页交易聚合统计 API + * + * 对接后端 /admin/dashboard/trade/* 聚合接口, 供 workspace widget 与 analytics 9 维度图表使用。 + * + * ## 参数约定 + * - **天数模式**: `days` 参数(近 N 天含今天) + * - **区间模式**: `start` + `end` 参数(yyyy-MM-dd, 均包含), 同时传时优先区间模式 + * + * 金额字段单位为分, 前端按需转元展示。 + */ +export const DashboardTradeApi = { + /** + * 交易概览(今日/昨日快捷 或 自定义区间含上期对比) + * @param date 'today' | 'yesterday' 快捷模式, 仅在未传 start/end 时生效 + * @param start yyyy-MM-dd(包含), 与 end 同时传时进入区间模式 + * @param end yyyy-MM-dd(包含) + */ + overview(params: { + date?: 'today' | 'yesterday'; + end?: string; + start?: string; + }): Promise> { + return defHttp.get({ url: '/admin/dashboard/trade/overview', params }); + }, + + /** + * 交易趋势(每日成交金额 + 笔数) + */ + trend(params: { days?: number; end?: string; start?: string }): Promise> { + return defHttp.get({ url: '/admin/dashboard/trade/trend', params }); + }, + + /** + * 退款趋势(每日退款金额 + 笔数) + */ + refundTrend(params: { days?: number; end?: string; start?: string }): Promise> { + return defHttp.get({ url: '/admin/dashboard/trade/refund-trend', params }); + }, + + /** + * 支付渠道分布(各渠道成交金额 + 笔数) + */ + providerDist(params: { days?: number; end?: string; start?: string }): Promise> { + return defHttp.get({ url: '/admin/dashboard/trade/provider-dist', params }); + }, + + /** + * 支付渠道成功率(各渠道 success_count / total_count * 100) + */ + providerSuccess(params: { days?: number; end?: string; start?: string }): Promise> { + return defHttp.get({ url: '/admin/dashboard/trade/provider-success', params }); + }, + + /** + * 24 小时时段分布(各小时成交金额 + 笔数, 已补齐 0-23) + */ + hourlyDist(params: { days?: number; end?: string; start?: string }): Promise> { + return defHttp.get({ url: '/admin/dashboard/trade/hourly-dist', params }); + }, + + /** + * 金额区间分桶(5 档: 0-50/50-200/200-1000/1000-5000/5000+, 已补齐) + */ + amountRange(params: { days?: number; end?: string; start?: string }): Promise> { + return defHttp.get({ url: '/admin/dashboard/trade/amount-range', params }); + }, + + /** + * 商户交易额排名 + * @param limit 返回前 N 名(默认 10, 上限 50) + */ + merchantRank(params: { days?: number; end?: string; limit?: number; start?: string }): Promise> { + return defHttp.get({ url: '/admin/dashboard/trade/merchant-rank', params }); + }, +}; + +/** 交易概览结果(金额单位: 分) */ +export interface TradeOverviewResult { + // ===== 本期 ===== + /** 成交金额(分) */ + successAmount?: number; + /** 成交笔数 */ + successCount?: number; + /** 退款金额(分) */ + refundAmount?: number; + /** 退款笔数 */ + refundCount?: number; + /** 总下单笔数(含成功/失败/关闭, 用于成功率分母, 按 create_time 口径) */ + totalOrders?: number; + // ===== 上期(用于环比, undefined 表示上期无数据) ===== + prevSuccessAmount?: number; + prevSuccessCount?: number; + prevRefundAmount?: number; + prevRefundCount?: number; + prevTotalOrders?: number; +} + +/** 交易趋势单项(日期 + 金额 + 笔数) */ +export interface TradeTrendItemResult { + /** 日期(yyyy-MM-dd) */ + date?: string; + /** 成交金额(分) */ + amount?: number; + /** 成交笔数 */ + count?: number; +} + +/** 退款趋势单项 */ +export interface RefundTrendItemResult { + date?: string; + amount?: number; + count?: number; +} + +/** 支付渠道分布单项 */ +export interface ProviderDistItemResult { + /** 支付渠道编码(如 wechat/alipay/union_pay) */ + provider?: string; + /** 成交金额(分) */ + amount?: number; + /** 成交笔数 */ + count?: number; +} + +/** 支付渠道成功率单项 */ +export interface ProviderSuccessItemResult { + /** 支付渠道编码 */ + provider?: string; + /** 成功率(0-100) */ + rate?: number; +} + +/** 24 小时时段分布单项 */ +export interface HourlyDistItemResult { + /** 小时(0-23) */ + hour?: number; + /** 成交金额(分) */ + amount?: number; + /** 成交笔数 */ + count?: number; +} + +/** 金额区间分桶单项 */ +export interface AmountRangeItemResult { + /** 区间标签(如 '0-50', '5000+') */ + bucket?: string; + /** 笔数 */ + count?: number; +} + +/** 商户交易额排名单项 */ +export interface MerchantRankItemResult { + /** 商户号 */ + mchNo?: string; + /** 商户名称 */ + merchantName?: string; + /** 成交金额(分) */ + amount?: number; + /** 成交笔数 */ + orders?: number; + /** 占比百分比(0-100) */ + proportion?: number; +} diff --git a/apps/daxpay-admin/src/enums/payment/index.ts b/apps/daxpay-admin/src/enums/payment/index.ts index c8ce4b2c0..712373ac6 100644 --- a/apps/daxpay-admin/src/enums/payment/index.ts +++ b/apps/daxpay-admin/src/enums/payment/index.ts @@ -1,2 +1,3 @@ export * from './channelEnum'; export * from './productEnum'; +export * from './providerEnum'; diff --git a/apps/daxpay-admin/src/enums/payment/providerEnum.ts b/apps/daxpay-admin/src/enums/payment/providerEnum.ts new file mode 100644 index 000000000..2f2e525db --- /dev/null +++ b/apps/daxpay-admin/src/enums/payment/providerEnum.ts @@ -0,0 +1,51 @@ +/** + * 支付渠道枚举 + * + * 对齐后端 PayProviderEnum, 表达付款方使用的钱包或支付渠道(C 端视角), 非支付通道。 + * 字典: pay_provider + */ +export enum ProviderEnum { + /** 聚合支付(收银台/一码多付等) */ + AGGREGATE_PAY = 'aggregate_pay', + /** 微信(C 端钱包渠道) */ + WECHAT = 'wechat', + /** 支付宝 */ + ALIPAY = 'alipay', + /** 银联 */ + UNION_PAY = 'union_pay', + /** Visa 卡组织 */ + VISA = 'visa', + /** 万事达卡组织 */ + MASTERCARD = 'mastercard', + /** 抖音支付 */ + DOUYIN = 'douyin', +} + +/** + * 支付渠道国际化Key映射 + * + * 用于支付渠道分布报表等"按用户付款方式统计"的场景, + * 与支付通道 channelEnum(系统对接通道) 区分。 + */ +export const providerI18nMap: Record = { + [ProviderEnum.AGGREGATE_PAY]: 'payment.provider.common.aggregatePay', + [ProviderEnum.WECHAT]: 'payment.provider.common.wechat', + [ProviderEnum.ALIPAY]: 'payment.provider.common.alipay', + [ProviderEnum.UNION_PAY]: 'payment.provider.common.unionPay', + [ProviderEnum.VISA]: 'payment.provider.common.visa', + [ProviderEnum.MASTERCARD]: 'payment.provider.common.mastercard', + [ProviderEnum.DOUYIN]: 'payment.provider.common.douyin', +}; + +/** + * 支付渠道默认名称映射(降级用, 正常走 i18n) + */ +export const providerNameMap: Record = { + [ProviderEnum.AGGREGATE_PAY]: '聚合支付', + [ProviderEnum.WECHAT]: '微信支付', + [ProviderEnum.ALIPAY]: '支付宝', + [ProviderEnum.UNION_PAY]: '银联', + [ProviderEnum.VISA]: 'Visa', + [ProviderEnum.MASTERCARD]: '万事达', + [ProviderEnum.DOUYIN]: '抖音支付', +}; diff --git a/apps/daxpay-admin/src/locales/langs/en-US/dashboard/workspace.json b/apps/daxpay-admin/src/locales/langs/en-US/dashboard/workspace.json index d234227f9..58ee6b871 100644 --- a/apps/daxpay-admin/src/locales/langs/en-US/dashboard/workspace.json +++ b/apps/daxpay-admin/src/locales/langs/en-US/dashboard/workspace.json @@ -18,7 +18,7 @@ "tradeOverview": "Trade Overview", "payOrder": "Payment Orders", "tradeTrend": "Trade Trend", - "channelDist": "Payment Channel", + "providerDist": "Payment Method Distribution", "noticeList": "Notices" }, "tradeOverview": { @@ -29,14 +29,6 @@ "refundAmount": "Refund Amount (CNY)", "refundCount": "Refund Count" }, - "payOrder": { - "developing": "Payment order management in development", - "status": { - "success": "Success", - "fail": "Failed", - "closed": "Closed" - } - }, "tradeTrend": { "last7days": "7 Days", "last30days": "30 Days" diff --git a/apps/daxpay-admin/src/locales/langs/en-US/payment/provider/common.json b/apps/daxpay-admin/src/locales/langs/en-US/payment/provider/common.json new file mode 100644 index 000000000..de20d2bef --- /dev/null +++ b/apps/daxpay-admin/src/locales/langs/en-US/payment/provider/common.json @@ -0,0 +1,9 @@ +{ + "aggregatePay": "Aggregate Pay", + "wechat": "WeChat Pay", + "alipay": "Alipay", + "unionPay": "UnionPay", + "visa": "Visa", + "mastercard": "Mastercard", + "douyin": "Douyin Pay" +} diff --git a/apps/daxpay-admin/src/locales/langs/id-ID/dashboard/workspace.json b/apps/daxpay-admin/src/locales/langs/id-ID/dashboard/workspace.json index b4f544917..56db4f808 100644 --- a/apps/daxpay-admin/src/locales/langs/id-ID/dashboard/workspace.json +++ b/apps/daxpay-admin/src/locales/langs/id-ID/dashboard/workspace.json @@ -18,7 +18,7 @@ "tradeOverview": "Ikhtisar Perdagangan", "payOrder": "Perintah Pembayaran", "tradeTrend": "Tren Perdagangan", - "channelDist": "Saluran Pembayaran", + "providerDist": "Distribusi Metode Pembayaran", "noticeList": "Pemberitahuan" }, "tradeOverview": { @@ -29,14 +29,6 @@ "refundAmount": "Jumlah Pengembalian Dana (CNY)", "refundCount": "Jumlah Pengembalian Dana" }, - "payOrder": { - "developing": "Manajemen pesanan pembayaran dalam pengembangan", - "status": { - "success": "Kesuksesan", - "fail": "Gagal", - "closed": "Tertutup" - } - }, "tradeTrend": { "last7days": "7 hari", "last30days": "30 Hari" diff --git a/apps/daxpay-admin/src/locales/langs/id-ID/payment/provider/common.json b/apps/daxpay-admin/src/locales/langs/id-ID/payment/provider/common.json new file mode 100644 index 000000000..c55d8ec77 --- /dev/null +++ b/apps/daxpay-admin/src/locales/langs/id-ID/payment/provider/common.json @@ -0,0 +1,9 @@ +{ + "aggregatePay": "Pembayaran Agregat", + "wechat": "Pembayaran WeChat", + "alipay": "Alipay", + "unionPay": "UnionPay", + "visa": "Visa", + "mastercard": "Mastercard", + "douyin": "Pembayaran Douyin" +} diff --git a/apps/daxpay-admin/src/locales/langs/ja-JP/dashboard/workspace.json b/apps/daxpay-admin/src/locales/langs/ja-JP/dashboard/workspace.json index 14122fb88..e272863f5 100644 --- a/apps/daxpay-admin/src/locales/langs/ja-JP/dashboard/workspace.json +++ b/apps/daxpay-admin/src/locales/langs/ja-JP/dashboard/workspace.json @@ -18,7 +18,7 @@ "tradeOverview": "貿易概要", "payOrder": "支払い命令", "tradeTrend": "貿易動向", - "channelDist": "支払いチャネル", + "providerDist": "決済方法分布", "noticeList": "お知らせ" }, "tradeOverview": { @@ -29,14 +29,6 @@ "refundAmount": "払い戻し金額 (CNY)", "refundCount": "払い戻し回数" }, - "payOrder": { - "developing": "支払い注文管理は開発中", - "status": { - "success": "成功", - "fail": "失敗した", - "closed": "閉店" - } - }, "tradeTrend": { "last7days": "7日間", "last30days": "30日" diff --git a/apps/daxpay-admin/src/locales/langs/ja-JP/payment/provider/common.json b/apps/daxpay-admin/src/locales/langs/ja-JP/payment/provider/common.json new file mode 100644 index 000000000..23b6dee1e --- /dev/null +++ b/apps/daxpay-admin/src/locales/langs/ja-JP/payment/provider/common.json @@ -0,0 +1,9 @@ +{ + "aggregatePay": "統合決済", + "wechat": "WeChat Pay", + "alipay": "Alipay", + "unionPay": "UnionPay", + "visa": "Visa", + "mastercard": "Mastercard", + "douyin": "Douyin Pay" +} diff --git a/apps/daxpay-admin/src/locales/langs/ko-KR/dashboard/workspace.json b/apps/daxpay-admin/src/locales/langs/ko-KR/dashboard/workspace.json index 1ba6fae4a..5ec4c139b 100644 --- a/apps/daxpay-admin/src/locales/langs/ko-KR/dashboard/workspace.json +++ b/apps/daxpay-admin/src/locales/langs/ko-KR/dashboard/workspace.json @@ -18,7 +18,7 @@ "tradeOverview": "무역개요", "payOrder": "지불 주문", "tradeTrend": "무역동향", - "channelDist": "결제 채널", + "providerDist": "결제 수단 분포", "noticeList": "공지사항" }, "tradeOverview": { @@ -29,14 +29,6 @@ "refundAmount": "환불 금액(CNY)", "refundCount": "환불 횟수" }, - "payOrder": { - "developing": "결제 주문 관리 개발 중", - "status": { - "success": "성공", - "fail": "실패한", - "closed": "닫은" - } - }, "tradeTrend": { "last7days": "7일", "last30days": "30일" diff --git a/apps/daxpay-admin/src/locales/langs/ko-KR/payment/provider/common.json b/apps/daxpay-admin/src/locales/langs/ko-KR/payment/provider/common.json new file mode 100644 index 000000000..c1b3bddba --- /dev/null +++ b/apps/daxpay-admin/src/locales/langs/ko-KR/payment/provider/common.json @@ -0,0 +1,9 @@ +{ + "aggregatePay": "통합 결제", + "wechat": "위챗페이", + "alipay": "알리페이", + "unionPay": "유니온페이", + "visa": "Visa", + "mastercard": "Mastercard", + "douyin": "두인페이" +} diff --git a/apps/daxpay-admin/src/locales/langs/ms-MY/dashboard/workspace.json b/apps/daxpay-admin/src/locales/langs/ms-MY/dashboard/workspace.json index f9453380f..cb32ac823 100644 --- a/apps/daxpay-admin/src/locales/langs/ms-MY/dashboard/workspace.json +++ b/apps/daxpay-admin/src/locales/langs/ms-MY/dashboard/workspace.json @@ -18,7 +18,7 @@ "tradeOverview": "Tinjauan Perdagangan", "payOrder": "Pesanan Pembayaran", "tradeTrend": "Trend Perdagangan", - "channelDist": "Saluran Pembayaran", + "providerDist": "Agihan Kaedah Pembayaran", "noticeList": "Notis" }, "tradeOverview": { @@ -29,14 +29,6 @@ "refundAmount": "Amaun Bayaran Balik (CNY)", "refundCount": "Kiraan Bayaran Balik" }, - "payOrder": { - "developing": "Pengurusan pesanan pembayaran dalam pembangunan", - "status": { - "success": "Kejayaan", - "fail": "gagal", - "closed": "tertutup" - } - }, "tradeTrend": { "last7days": "7 Hari", "last30days": "30 Hari" diff --git a/apps/daxpay-admin/src/locales/langs/ms-MY/payment/provider/common.json b/apps/daxpay-admin/src/locales/langs/ms-MY/payment/provider/common.json new file mode 100644 index 000000000..6775ac028 --- /dev/null +++ b/apps/daxpay-admin/src/locales/langs/ms-MY/payment/provider/common.json @@ -0,0 +1,9 @@ +{ + "aggregatePay": "Bayaran Agregat", + "wechat": "WeChat Pay", + "alipay": "Alipay", + "unionPay": "UnionPay", + "visa": "Visa", + "mastercard": "Mastercard", + "douyin": "Douyin Pay" +} diff --git a/apps/daxpay-admin/src/locales/langs/th-TH/dashboard/workspace.json b/apps/daxpay-admin/src/locales/langs/th-TH/dashboard/workspace.json index 1e47fc7b3..774a9e0dd 100644 --- a/apps/daxpay-admin/src/locales/langs/th-TH/dashboard/workspace.json +++ b/apps/daxpay-admin/src/locales/langs/th-TH/dashboard/workspace.json @@ -18,7 +18,7 @@ "tradeOverview": "ภาพรวมการค้า", "payOrder": "คำสั่งการชำระเงิน", "tradeTrend": "แนวโน้มการค้า", - "channelDist": "ช่องทางการชำระเงิน", + "providerDist": "การกระจายวิธีการชำระเงิน", "noticeList": "ประกาศ" }, "tradeOverview": { @@ -29,14 +29,6 @@ "refundAmount": "จำนวนเงินคืน (CNY)", "refundCount": "จำนวนการคืนเงิน" }, - "payOrder": { - "developing": "การจัดการคำสั่งการชำระเงินอยู่ระหว่างการพัฒนา", - "status": { - "success": "ความสำเร็จ", - "fail": "ล้มเหลว", - "closed": "ปิด" - } - }, "tradeTrend": { "last7days": "7 วัน", "last30days": "30 วัน" diff --git a/apps/daxpay-admin/src/locales/langs/th-TH/payment/provider/common.json b/apps/daxpay-admin/src/locales/langs/th-TH/payment/provider/common.json new file mode 100644 index 000000000..d9ad97efd --- /dev/null +++ b/apps/daxpay-admin/src/locales/langs/th-TH/payment/provider/common.json @@ -0,0 +1,9 @@ +{ + "aggregatePay": "ชำระเงินแบบรวม", + "wechat": "WeChat Pay", + "alipay": "Alipay", + "unionPay": "UnionPay", + "visa": "Visa", + "mastercard": "Mastercard", + "douyin": "Douyin Pay" +} diff --git a/apps/daxpay-admin/src/locales/langs/vi-VN/dashboard/workspace.json b/apps/daxpay-admin/src/locales/langs/vi-VN/dashboard/workspace.json index b03678917..fe2448028 100644 --- a/apps/daxpay-admin/src/locales/langs/vi-VN/dashboard/workspace.json +++ b/apps/daxpay-admin/src/locales/langs/vi-VN/dashboard/workspace.json @@ -18,7 +18,7 @@ "tradeOverview": "Tổng quan thương mại", "payOrder": "Lệnh thanh toán", "tradeTrend": "Xu hướng thương mại", - "channelDist": "Kênh thanh toán", + "providerDist": "Phân bố phương thức thanh toán", "noticeList": "Thông báo" }, "tradeOverview": { @@ -29,14 +29,6 @@ "refundAmount": "Số tiền hoàn lại (CNY)", "refundCount": "Số tiền hoàn lại" }, - "payOrder": { - "developing": "Quản lý lệnh thanh toán đang phát triển", - "status": { - "success": "Thành công", - "fail": "Thất bại", - "closed": "Đã đóng" - } - }, "tradeTrend": { "last7days": "7 ngày", "last30days": "30 ngày" diff --git a/apps/daxpay-admin/src/locales/langs/vi-VN/payment/provider/common.json b/apps/daxpay-admin/src/locales/langs/vi-VN/payment/provider/common.json new file mode 100644 index 000000000..5ab331cb7 --- /dev/null +++ b/apps/daxpay-admin/src/locales/langs/vi-VN/payment/provider/common.json @@ -0,0 +1,9 @@ +{ + "aggregatePay": "Thanh toán tổng hợp", + "wechat": "WeChat Pay", + "alipay": "Alipay", + "unionPay": "UnionPay", + "visa": "Visa", + "mastercard": "Mastercard", + "douyin": "Douyin Pay" +} diff --git a/apps/daxpay-admin/src/locales/langs/zh-CN/dashboard/workspace.json b/apps/daxpay-admin/src/locales/langs/zh-CN/dashboard/workspace.json index f5c3c9551..5aac6c0bd 100644 --- a/apps/daxpay-admin/src/locales/langs/zh-CN/dashboard/workspace.json +++ b/apps/daxpay-admin/src/locales/langs/zh-CN/dashboard/workspace.json @@ -18,7 +18,7 @@ "tradeOverview": "交易概览", "payOrder": "支付订单", "tradeTrend": "交易趋势", - "channelDist": "支付渠道", + "providerDist": "支付渠道分布", "noticeList": "系统公告" }, "tradeOverview": { @@ -29,14 +29,6 @@ "refundAmount": "退款金额(元)", "refundCount": "退款笔数" }, - "payOrder": { - "developing": "支付订单管理开发中", - "status": { - "success": "成功", - "fail": "失败", - "closed": "已关闭" - } - }, "tradeTrend": { "last7days": "近 7 天", "last30days": "近 30 天" diff --git a/apps/daxpay-admin/src/locales/langs/zh-CN/payment/provider/common.json b/apps/daxpay-admin/src/locales/langs/zh-CN/payment/provider/common.json new file mode 100644 index 000000000..651faee27 --- /dev/null +++ b/apps/daxpay-admin/src/locales/langs/zh-CN/payment/provider/common.json @@ -0,0 +1,9 @@ +{ + "aggregatePay": "聚合支付", + "wechat": "微信支付", + "alipay": "支付宝", + "unionPay": "银联", + "visa": "Visa", + "mastercard": "万事达", + "douyin": "抖音支付" +} diff --git a/apps/daxpay-admin/src/locales/langs/zh-HK/dashboard/workspace.json b/apps/daxpay-admin/src/locales/langs/zh-HK/dashboard/workspace.json index b49b39395..308cfab64 100644 --- a/apps/daxpay-admin/src/locales/langs/zh-HK/dashboard/workspace.json +++ b/apps/daxpay-admin/src/locales/langs/zh-HK/dashboard/workspace.json @@ -18,7 +18,7 @@ "tradeOverview": "交易概覽", "payOrder": "支付訂單", "tradeTrend": "交易趨勢", - "channelDist": "支付渠道", + "providerDist": "支付渠道分佈", "noticeList": "系統公告" }, "tradeOverview": { @@ -29,14 +29,6 @@ "refundAmount": "退款金額(元)", "refundCount": "退款筆數" }, - "payOrder": { - "developing": "支付訂單管理開發中", - "status": { - "success": "成功", - "fail": "失敗", - "closed": "已關閉" - } - }, "tradeTrend": { "last7days": "近 7 天", "last30days": "近 30 天" diff --git a/apps/daxpay-admin/src/locales/langs/zh-HK/payment/provider/common.json b/apps/daxpay-admin/src/locales/langs/zh-HK/payment/provider/common.json new file mode 100644 index 000000000..093b8415d --- /dev/null +++ b/apps/daxpay-admin/src/locales/langs/zh-HK/payment/provider/common.json @@ -0,0 +1,9 @@ +{ + "aggregatePay": "聚合支付", + "wechat": "微信支付", + "alipay": "支付寶", + "unionPay": "銀聯", + "visa": "Visa", + "mastercard": "萬事達", + "douyin": "抖音支付" +} diff --git a/apps/daxpay-admin/src/locales/langs/zh-TW/dashboard/workspace.json b/apps/daxpay-admin/src/locales/langs/zh-TW/dashboard/workspace.json index 518618cc0..780d9cd1e 100644 --- a/apps/daxpay-admin/src/locales/langs/zh-TW/dashboard/workspace.json +++ b/apps/daxpay-admin/src/locales/langs/zh-TW/dashboard/workspace.json @@ -18,7 +18,7 @@ "tradeOverview": "交易概覽", "payOrder": "支付訂單", "tradeTrend": "交易趨勢", - "channelDist": "支付渠道", + "providerDist": "支付渠道分佈", "noticeList": "系統公告" }, "tradeOverview": { @@ -29,14 +29,6 @@ "refundAmount": "退款金額(元)", "refundCount": "退款筆數" }, - "payOrder": { - "developing": "支付訂單管理開發中", - "status": { - "success": "成功", - "fail": "失敗", - "closed": "已關閉" - } - }, "tradeTrend": { "last7days": "近 7 天", "last30days": "近 30 天" diff --git a/apps/daxpay-admin/src/locales/langs/zh-TW/payment/provider/common.json b/apps/daxpay-admin/src/locales/langs/zh-TW/payment/provider/common.json new file mode 100644 index 000000000..093b8415d --- /dev/null +++ b/apps/daxpay-admin/src/locales/langs/zh-TW/payment/provider/common.json @@ -0,0 +1,9 @@ +{ + "aggregatePay": "聚合支付", + "wechat": "微信支付", + "alipay": "支付寶", + "unionPay": "銀聯", + "visa": "Visa", + "mastercard": "萬事達", + "douyin": "抖音支付" +} diff --git a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-amount-range.vue b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-amount-range.vue index 7040c5c1b..28c62fb6b 100644 --- a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-amount-range.vue +++ b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-amount-range.vue @@ -10,11 +10,14 @@ const props = withDefaults(defineProps(), { data: undefined, + loading: false, }); interface Props { /** 单笔金额区间分桶(用户消费特征) */ data?: AmountRangeItem[]; + /** 加载中(显示骨架屏) */ + loading?: boolean; } const chartRef = ref(); @@ -51,6 +54,7 @@ - + + diff --git a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-channel-success.vue b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-channel-success.vue index f1636eaa4..a5fbeb2a3 100644 --- a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-channel-success.vue +++ b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-channel-success.vue @@ -10,11 +10,14 @@ const props = withDefaults(defineProps(), { data: undefined, + loading: false, }); interface Props { /** 各支付渠道成功率(横向对比,一眼看出渠道质量差异) */ data?: ChannelSuccessItem[]; + /** 加载中(显示骨架屏) */ + loading?: boolean; } const chartRef = ref(); @@ -57,6 +60,7 @@ - + + diff --git a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-channel-volume.vue b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-channel-volume.vue index 6c73f4fd1..852ba0128 100644 --- a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-channel-volume.vue +++ b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-channel-volume.vue @@ -10,11 +10,14 @@ const props = withDefaults(defineProps(), { data: undefined, + loading: false, }); interface Props { /** 各支付通道交易笔数 */ data?: NameValue[]; + /** 加载中(显示骨架屏) */ + loading?: boolean; } const chartRef = ref(); @@ -51,6 +54,7 @@ - + + diff --git a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-hourly-dist.vue b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-hourly-dist.vue index 6e8afd46d..c66ca3042 100644 --- a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-hourly-dist.vue +++ b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-hourly-dist.vue @@ -8,11 +8,14 @@ const props = withDefaults(defineProps(), { data: undefined, + loading: false, }); interface Props { /** 24 小时交易分布(长度 24 数组,下标即小时) */ data?: number[]; + /** 加载中(显示骨架屏) */ + loading?: boolean; } const chartRef = ref(); @@ -46,6 +49,7 @@ - + + diff --git a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-merchant-rank.vue b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-merchant-rank.vue index e5a698174..662edbf87 100644 --- a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-merchant-rank.vue +++ b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-merchant-rank.vue @@ -9,11 +9,14 @@ const props = withDefaults(defineProps(), { data: () => [], + loading: false, }); interface Props { /** 商户交易额排名 Top10 */ data?: MerchantRankItem[]; + /** 加载中(显示骨架屏) */ + loading?: boolean; } // 表格列定义(排名 / 商户 / 交易额 / 笔数 / 占比) @@ -45,6 +48,7 @@ diff --git a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-overview.vue b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-overview.vue index 223a329d8..f666b11a7 100644 --- a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-overview.vue +++ b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-overview.vue @@ -7,11 +7,14 @@ const props = withDefaults(defineProps(), { data: () => [], + loading: false, }); interface Props { /** 6 个指标卡片数据(规模 + 质量) */ data?: OverviewStat[]; + /** 加载中(显示骨架屏) */ + loading?: boolean; } // 数值千分位格式化 @@ -22,7 +25,14 @@ diff --git a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-refund-trend.vue b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-refund-trend.vue index 22f44cbfe..cf96547d5 100644 --- a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-refund-trend.vue +++ b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-refund-trend.vue @@ -8,11 +8,14 @@ const props = withDefaults(defineProps(), { data: undefined, + loading: false, }); interface Props { /** 退款趋势(日期 + 退款额) */ data?: { amounts: number[]; dates: string[] }; + /** 加载中(显示骨架屏) */ + loading?: boolean; } const chartRef = ref(); @@ -51,6 +54,7 @@ - + + diff --git a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-trade-trend.vue b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-trade-trend.vue index adcd94510..f1c4305b5 100644 --- a/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-trade-trend.vue +++ b/apps/daxpay-admin/src/views/dashboard/analytics/components/analysis-trade-trend.vue @@ -10,11 +10,14 @@ const props = withDefaults(defineProps(), { data: undefined, + loading: false, }); interface Props { /** 交易趋势序列(日期 + 多度量) */ data?: TrendSeries; + /** 加载中(显示骨架屏) */ + loading?: boolean; } // 度量切换:交易额 / 交易笔数 / 客单价 @@ -69,6 +72,7 @@ - + + diff --git a/apps/daxpay-admin/src/views/dashboard/analytics/composables/useAnalyticsData.ts b/apps/daxpay-admin/src/views/dashboard/analytics/composables/useAnalyticsData.ts index 7da8c6cde..4514374d7 100644 --- a/apps/daxpay-admin/src/views/dashboard/analytics/composables/useAnalyticsData.ts +++ b/apps/daxpay-admin/src/views/dashboard/analytics/composables/useAnalyticsData.ts @@ -6,6 +6,11 @@ import { $t } from '@vben/locales'; import dayjs from 'dayjs'; +import { DashboardTradeApi } from '#/api/payment/dashboard/trade-dashboard.api'; +import { providerI18nMap, providerNameMap } from '#/enums/payment/providerEnum'; + +import { emptyAnalyticsData } from '../types'; + /** 预设范围 → [start, end](YYYY-MM-DD);本月取月初到今天(未来无数据) */ function computePreset(key: PresetKey): DateRange { const today = dayjs(); @@ -25,128 +30,47 @@ function computePreset(key: PresetKey): DateRange { } } -/** 按 [start, end] 生成日期标签(MM-DD,含首尾) */ -function genDateLabels(start: string, end: string): string[] { - const labels: string[] = []; - let cur = dayjs(start); - const last = dayjs(end); - while (cur.isBefore(last) || cur.isSame(last, 'day')) { - labels.push(cur.format('MM-DD')); - cur = cur.add(1, 'day'); +/** 支付渠道编码 → 展示名: 优先 i18n 映射, 无映射时降级原编码 */ +function providerLabel(provider?: string): string { + if (!provider) return '-'; + const i18nKey = providerI18nMap[provider]; + if (i18nKey) { + const text = $t(i18nKey); + if (text && text !== i18nKey) return text; } - return labels; + return providerNameMap[provider] ?? provider; } -/** 基于索引的稳定波动函数(同输入同输出,避免每次切换数据跳变) */ -function wave(i: number, seed: number): number { - return Math.sin(i * 0.8 + seed) * 0.5 + Math.cos(i * 1.3 + seed) * 0.3; +/** 金额分(后端) → 元(前端展示), 保留整数 */ +function fenToYuan(fen?: number): number { + if (!fen || fen <= 0) return 0; + return Math.round(fen / 100); } -/** 按日期范围构造全量 mock 数据(后端聚合 API 就绪后替换为真实请求) */ -function buildData(start: string, end: string): AnalyticsData { - const dates = genDateLabels(start, end); - const n = dates.length; - - // 交易额(元):基线 + 上升趋势 + 周期波动 - const amounts = dates.map((_, i) => Math.round(50_000 + i * 600 + wave(i, 1) * 12_000)); - // 交易笔数 - const orders = dates.map((_, i) => Math.round(800 + i * 8 + wave(i, 2) * 120)); - // 客单价 = 交易额 / 笔数 - const avgAmounts = amounts.map((a, i) => Math.round(a / (orders[i] || 1))); - - // 汇总指标 - const totalAmount = amounts.reduce((s, v) => s + v, 0); - const totalOrders = orders.reduce((s, v) => s + v, 0); - const refundAmount = Math.round(totalAmount * 0.038); - const avgAmount = Math.round(totalAmount / totalOrders); - - // 环比百分比(mock,随范围稳定) - const ratio = (base: number, seed: number) => Math.round((base + wave(n, seed) * 5) * 10) / 10; - - // 退款趋势(元) - const refundTrendAmounts = dates.map((_, i) => Math.round(1800 + wave(i, 3) * 600 + i * 20)); - - // 24h 时段分布:模拟双高峰(午间 12 点、晚间 20 点),凌晨低谷 - const hourlyDist = Array.from({ length: 24 }).map((_, h) => { - const noonPeak = Math.exp(-Math.pow((h - 12) / 4, 2)) * 1000; - const eveningPeak = Math.exp(-Math.pow((h - 20) / 3, 2)) * 800; - const noise = wave(h, 4) * 100; - return Math.max(50, Math.round(noonPeak + eveningPeak + noise + 100)); - }); - - return { - overview: [ - { key: 'totalAmount', value: totalAmount, prefix: '¥', chainRatio: ratio(8.5, 1) }, - { key: 'totalOrders', value: totalOrders, chainRatio: ratio(6.2, 2) }, - { key: 'avgAmount', value: avgAmount, prefix: '¥', chainRatio: ratio(2.1, 3) }, - { key: 'successRate', value: 97.6, suffix: '%', chainRatio: ratio(0.3, 4) }, - { key: 'refundAmount', value: refundAmount, prefix: '¥', chainRatio: ratio(-3.4, 5) }, - { key: 'refundRate', value: 3.8, suffix: '%', chainRatio: ratio(-1.2, 6) }, - ], - tradeTrend: { amounts, avgAmounts, dates, orders }, - payMethod: [ - { name: $t('payment.channel.common.wechat'), value: 38 }, - { name: $t('payment.channel.common.alipay'), value: 28 }, - { name: $t('payment.channel.common.unionPay'), value: 18 }, - { name: $t('payment.channel.common.visa'), value: 8 }, - { name: $t('payment.channel.common.mastercard'), value: 5 }, - { name: $t('payment.channel.common.douyin'), value: 3 }, - ], - channelSuccess: [ - { name: $t('payment.channel.common.alipay'), rate: 99.2 }, - { name: $t('payment.channel.common.wechat'), rate: 98.7 }, - { name: $t('payment.channel.common.unionPay'), rate: 97.5 }, - { name: $t('payment.channel.common.douyin'), rate: 96.3 }, - { name: $t('payment.channel.common.visa'), rate: 95.8 }, - { name: $t('payment.channel.common.mastercard'), rate: 94.6 }, - ], - hourlyDist, - amountRange: [ - { count: 3200, range: '0-50' }, - { count: 5800, range: '50-200' }, - { count: 4200, range: '200-1000' }, - { count: 1800, range: '1000-5000' }, - { count: 600, range: '5000+' }, - ], - channelVolume: [ - { name: $t('payment.channel.common.wechat'), value: 45_000 }, - { name: $t('payment.channel.common.alipay'), value: 32_000 }, - { name: $t('payment.channel.common.unionPay'), value: 12_000 }, - { name: $t('payment.channel.common.visa'), value: 8000 }, - { name: $t('payment.channel.common.mastercard'), value: 6000 }, - { name: $t('payment.channel.common.douyin'), value: 3000 }, - ], - refundTrend: { amounts: refundTrendAmounts, dates }, - merchantRank: [ - { amount: 520_000, merchantName: '云端科技有限公司', orders: 12_300, proportion: 18.5 }, - { amount: 438_000, merchantName: '数聚信息科技', orders: 9800, proportion: 15.6 }, - { amount: 362_000, merchantName: '极客电子商务', orders: 8600, proportion: 12.9 }, - { amount: 298_000, merchantName: '智慧零售集团', orders: 7200, proportion: 10.6 }, - { amount: 245_000, merchantName: '新橙科技', orders: 5900, proportion: 8.7 }, - { amount: 198_000, merchantName: '环球贸易有限公司', orders: 4700, proportion: 7.1 }, - { amount: 165_000, merchantName: '蓝海传媒', orders: 3900, proportion: 5.9 }, - { amount: 132_000, merchantName: '星辰网络', orders: 3100, proportion: 4.7 }, - { amount: 98_000, merchantName: '迅捷物流', orders: 2300, proportion: 3.5 }, - { amount: 76_000, merchantName: '橙意生活', orders: 1800, proportion: 2.7 }, - ], - }; +/** + * 环比百分比计算 + * @returns null 表示无法计算(prev 缺数据或为 0) + */ +function chainRatio(curr?: number, prev?: number): null | number { + if (curr === undefined || prev === undefined || prev === 0) return null; + return Math.round(((curr - prev) / prev) * 1000) / 10; } +/** + * 分析页聚合数据异步加载 composable + * + * 按 dateRange 触发 8 个 API 并发加载(Promise.allSettled), 单个失败时该字段保留空, + * 不影响其他图表。chainRatio(环比) 由前端基于后端返回的 curr/prev 字段计算。 + */ export function useAnalyticsData() { - // 当前模式(预设 key 或 'custom' 自定义) const activePreset = ref('last7days'); - // 自定义日期范围(仅 custom 模式生效) const customRange = ref(undefined); - - // 是否自定义模式(控制 RangePicker 启用/禁用) const isCustom = computed(() => activePreset.value === 'custom'); - // 选预设时清空自定义范围(避免残留) watch(activePreset, (key) => { if (key !== 'custom') customRange.value = undefined; }); - // 真实数据源:custom 走自定义范围(未选时兜底近7天),否则走预设 const dateRange = computed(() => { if (activePreset.value === 'custom') { return customRange.value ?? computePreset('last7days'); @@ -154,15 +78,172 @@ export function useAnalyticsData() { return computePreset(activePreset.value); }); - // 副标题:动态日期摘要(与右侧选择器联动) const subtitle = computed(() => { const [start, end] = dateRange.value; const days = dayjs(end).diff(dayjs(start), 'day') + 1; return $t('dashboard.analytics.subtitle', { days, end, start }); }); - // 按日期范围派生的全量数据 - const data = computed(() => buildData(dateRange.value[0], dateRange.value[1])); + const data = ref(emptyAnalyticsData()); + const loading = ref(false); - return { activePreset, customRange, data, isCustom, subtitle }; + /** 公共参数(start/end 同时传时优先区间模式) */ + function commonParams() { + const [start, end] = dateRange.value; + return { end, start }; + } + + /** 并发加载所有维度, allSettled 保证单个失败不阻塞其他 */ + async function load() { + loading.value = true; + try { + const params = commonParams(); + const results = await Promise.allSettled([ + DashboardTradeApi.overview(params), + DashboardTradeApi.trend(params), + DashboardTradeApi.refundTrend(params), + DashboardTradeApi.providerDist(params), + DashboardTradeApi.providerSuccess(params), + DashboardTradeApi.hourlyDist(params), + DashboardTradeApi.amountRange(params), + DashboardTradeApi.merchantRank({ ...params, limit: 10 }), + ]); + + // 失败的维度单独记录到控制台(开发期可见, 生产静默) + const labels = ['overview', 'trend', 'refundTrend', 'providerDist', 'providerSuccess', 'hourlyDist', 'amountRange', 'merchantRank'] as const; + results.forEach((r, i) => { + if (r.status === 'rejected') console.warn(`[analytics] ${labels[i]} 加载失败:`, r.reason); + }); + + const overviewRes = results[0].status === 'fulfilled' ? results[0].value?.data : undefined; + const trendRes = results[1].status === 'fulfilled' ? results[1].value?.data : undefined; + const refundTrendRes = results[2].status === 'fulfilled' ? results[2].value?.data : undefined; + const providerDistRes = results[3].status === 'fulfilled' ? results[3].value?.data : undefined; + const providerSuccessRes = results[4].status === 'fulfilled' ? results[4].value?.data : undefined; + const hourlyDistRes = results[5].status === 'fulfilled' ? results[5].value?.data : undefined; + const amountRangeRes = results[6].status === 'fulfilled' ? results[6].value?.data : undefined; + const merchantRankRes = results[7].status === 'fulfilled' ? results[7].value?.data : undefined; + + data.value = buildAnalyticsData({ + amountRangeRes, + hourlyDistRes, + merchantRankRes, + overviewRes, + providerDistRes, + providerSuccessRes, + refundTrendRes, + trendRes, + }); + } finally { + loading.value = false; + } + } + + // dateRange 变化即触发加载 + watch(dateRange, load, { immediate: true }); + + return { activePreset, customRange, data, isCustom, loading, subtitle }; +} + +/** 将各 API 响应组装为 AnalyticsData */ +function buildAnalyticsData(input: { + amountRangeRes?: { bucket?: string; count?: number }[]; + hourlyDistRes?: { amount?: number; count?: number; hour?: number }[]; + merchantRankRes?: { amount?: number; merchantName?: string; orders?: number; proportion?: number }[]; + overviewRes?: { + prevRefundAmount?: number; + prevRefundCount?: number; + prevSuccessAmount?: number; + prevSuccessCount?: number; + prevTotalOrders?: number; + refundAmount?: number; + refundCount?: number; + successAmount?: number; + successCount?: number; + totalOrders?: number; + }; + providerDistRes?: { amount?: number; count?: number; provider?: string }[]; + providerSuccessRes?: { provider?: string; rate?: number }[]; + refundTrendRes?: { amount?: number; count?: number; date?: string }[]; + trendRes?: { amount?: number; count?: number; date?: string }[]; +}): AnalyticsData { + const { overviewRes, trendRes, refundTrendRes, providerDistRes, providerSuccessRes, hourlyDistRes, amountRangeRes, merchantRankRes } = input; + + // ===== overview 6 卡片(含环比) ===== + const ov = overviewRes ?? {}; + const successAmountYuan = fenToYuan(ov.successAmount); + const refundAmountYuan = fenToYuan(ov.refundAmount); + const successCount = ov.successCount ?? 0; + const totalOrders = ov.totalOrders ?? 0; + const avgAmount = successCount > 0 ? Math.round((successAmountYuan / successCount) * 100) / 100 : 0; + // 成功率口径: success_count / total_orders * 100(分子分母时间基准不同, 见后端 Mapper 注释) + const successRate = totalOrders > 0 ? Math.round((successCount / totalOrders) * 1000) / 10 : 0; + const refundRate = successAmountYuan > 0 ? Math.round((refundAmountYuan / successAmountYuan) * 1000) / 10 : 0; + + const overview = [ + { key: 'totalAmount', value: successAmountYuan, prefix: '¥', chainRatio: chainRatio(ov.successAmount, ov.prevSuccessAmount) }, + { key: 'totalOrders', value: successCount, chainRatio: chainRatio(ov.successCount, ov.prevSuccessCount) }, + { key: 'avgAmount', value: avgAmount, prefix: '¥', chainRatio: chainRatio(avgAmount, ov.prevSuccessAmount && ov.prevSuccessCount ? Math.round((ov.prevSuccessAmount / 100 / ov.prevSuccessCount) * 100) / 100 : undefined) }, + { key: 'successRate', value: successRate, suffix: '%', chainRatio: chainRatio(ov.successCount && ov.totalOrders ? successRate : undefined, ov.prevSuccessCount && ov.prevTotalOrders ? Math.round((ov.prevSuccessCount / ov.prevTotalOrders) * 1000) / 10 : undefined) }, + { key: 'refundAmount', value: refundAmountYuan, prefix: '¥', chainRatio: chainRatio(ov.refundAmount, ov.prevRefundAmount) }, + { key: 'refundRate', value: refundRate, suffix: '%', chainRatio: null }, + ]; + + // ===== tradeTrend(按日期 + 多度量) ===== + // dates 保留 yyyy-MM-dd 原值(与 a-tooltip / 前端期望一致), amounts/orders 取后端原值, avgAmounts 派生 + const trendList = trendRes ?? []; + const tradeTrend = { + amounts: trendList.map((i) => fenToYuan(i.amount)), + avgAmounts: trendList.map((i) => { + const yuan = fenToYuan(i.amount); + const cnt = i.count ?? 0; + return cnt > 0 ? Math.round((yuan / cnt) * 100) / 100 : 0; + }), + dates: trendList.map((i) => i.date ?? ''), + orders: trendList.map((i) => i.count ?? 0), + }; + + // ===== refundTrend(按日期) ===== + const refundTrend = { + amounts: (refundTrendRes ?? []).map((i) => fenToYuan(i.amount)), + dates: (refundTrendRes ?? []).map((i) => i.date ?? ''), + }; + + // ===== payMethod(支付方式占比饼图, 用 provider amount) ===== + const payMethod = (providerDistRes ?? []).map((i) => ({ + name: providerLabel(i.provider), + value: fenToYuan(i.amount), + })); + + // ===== channelVolume(渠道交易量柱状图, 用 provider count) ===== + const channelVolume = (providerDistRes ?? []).map((i) => ({ + name: providerLabel(i.provider), + value: i.count ?? 0, + })); + + // ===== channelSuccess(渠道成功率) ===== + const channelSuccess = (providerSuccessRes ?? []).map((i) => ({ + name: providerLabel(i.provider), + rate: i.rate ?? 0, + })); + + // ===== hourlyDist(24 小时时段, 取 count 数组) ===== + // 后端返回 0-23 已补齐, 取 count 字段(原前端约定: 柱状图值) + const hourlyDist = (hourlyDistRes ?? []).map((i) => i.count ?? 0); + + // ===== amountRange(金额区间分桶) ===== + const amountRange = (amountRangeRes ?? []).map((i) => ({ + count: i.count ?? 0, + range: i.bucket ?? '', + })); + + // ===== merchantRank ===== + const merchantRank = (merchantRankRes ?? []).map((i) => ({ + amount: fenToYuan(i.amount), + merchantName: i.merchantName ?? '-', + orders: i.orders ?? 0, + proportion: i.proportion ?? 0, + })); + + return { amountRange, channelSuccess, channelVolume, hourlyDist, merchantRank, overview, payMethod, refundTrend, tradeTrend }; } diff --git a/apps/daxpay-admin/src/views/dashboard/analytics/index.vue b/apps/daxpay-admin/src/views/dashboard/analytics/index.vue index 7f3072d5f..6fb6d48b5 100644 --- a/apps/daxpay-admin/src/views/dashboard/analytics/index.vue +++ b/apps/daxpay-admin/src/views/dashboard/analytics/index.vue @@ -14,8 +14,8 @@ defineOptions({ name: 'Analytics' }); - // 全局时间范围(预设 + 自定义互斥)+ 派生数据(mock,后端聚合 API 就绪后替换) - const { activePreset, customRange, data, isCustom, subtitle } = useAnalyticsData(); + // 全局时间范围(预设 + 自定义互斥)+ 派生数据(异步加载, 8 个维度并发) + const { activePreset, customRange, data, isCustom, loading, subtitle } = useAnalyticsData();