Переглянути джерело

Refactor order and recharge handling in composables

- Replaced `useTopupPopup` with `useRecharge` in `useOrderPopup` to streamline the recharge process after order confirmation.
- Updated the `confirm` function to be asynchronous, ensuring proper handling of the recharge popup.
- Modified `useTopupPopup` to initialize with empty packages and methods, allowing for dynamic injection from `useRecharge`.
- Adjusted the `mine` page to utilize the new `openRecharge` method for handling recharge actions.
0es 4 місяців тому
батько
коміт
d42132f783

+ 19 - 0
app/api/wallet.ts

@@ -0,0 +1,19 @@
+/**
+ * Wallet / Recharge API module
+ * 钱包充值配置等相关接口
+ */
+
+import { http } from '~/utils/request'
+import type { WalletRechargeConfigVO } from '~/types/api'
+
+export const walletApi = {
+  /**
+   * 获取浏览器端钱包充值配置
+   * 对应后端接口:POST /wallet/recharge/browser/config
+   */
+  getRechargeConfig() {
+    return http.post<WalletRechargeConfigVO>('/wallet/recharge/browser/config')
+  },
+}
+
+

+ 4 - 3
app/composables/useOrderPopup.ts

@@ -62,7 +62,7 @@ const state = reactive<OrderPopupState>({
   specificQuantity: undefined,
 })
 
-const { open: openTopupPopup } = useTopupPopup()
+const { openRecharge } = useRecharge()
 const confirmHandler = ref<((payload: OrderPopupResult) => void) | null>(null)
 
 const totalPrice = computed(() => state.quantity * state.rate)
@@ -117,7 +117,7 @@ const close = () => {
   state.visible = false
 }
 
-const confirm = () => {
+const confirm = async () => {
   const handler = confirmHandler.value
   const payload = buildResult()
 
@@ -125,7 +125,8 @@ const confirm = () => {
     handler(payload)
   }
 
-  openTopupPopup()
+  // 下单确认后,统一通过 useRecharge 打开充值弹窗
+  await openRecharge()
 
   // close()
 }

+ 75 - 0
app/composables/useRecharge.ts

@@ -0,0 +1,75 @@
+import { computed } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useWalletRechargeStore } from '~/stores/walletRecharge'
+import { useAuth } from './useAuth'
+import { useTopupPopup } from './useTopupPopup'
+import { formatNumber } from '~/utils/helpers'
+
+// 充值流程组合式函数
+// - 从后端加载充值配置并缓存在 Pinia
+// - 把后端字段映射到 Topup 弹窗所需的档位结构
+// - 统一通过 useTopupPopup 打开充值弹窗
+export const useRecharge = () => {
+  const walletRechargeStore = useWalletRechargeStore()
+  const { items, loading } = storeToRefs(walletRechargeStore)
+
+  const { wallet } = useAuth()
+  const { open: openTopupPopup } = useTopupPopup()
+
+  const hasConfig = computed(() => items.value.length > 0)
+
+  const ensureConfig = async (force = false) => {
+    if (!force && walletRechargeStore.initialized && items.value.length > 0)
+      return
+
+    await walletRechargeStore.loadConfig(force)
+  }
+
+  /**
+   * 打开充值弹窗:
+   * - 确保已经加载充值配置
+   * - 按照 Topup 弹窗要求的结构构建档位数据
+   */
+  const openRecharge = async () => {
+    await ensureConfig()
+
+    const configItems = items.value
+    if (!configItems || configItems.length === 0) {
+      console.warn('Wallet recharge config is empty')
+      return
+    }
+
+    const balance = wallet.value?.diamond ?? 0
+
+    const packages = configItems.map(item => ({
+      id: item.id,
+      // UI 内部字段为 diamonds,这里保持与后端 diamond 一一对应
+      diamonds: item.diamond,
+      // 金额使用千分位展示,货币前缀在组件内处理(IDR {{ priceLabel }})
+      priceLabel: formatNumber(item.amount),
+    }))
+
+    // 支付方式目前前端写死;如果后端后续下发支付方式,可以从接口映射
+    const methods = [
+      { id: 'payermax', name: 'PayerMax' },
+    ]
+
+    openTopupPopup({
+      balance,
+      packages,
+      methods,
+      defaultPackageId: packages[0]?.id,
+      defaultMethodId: methods[0]?.id,
+    })
+  }
+
+  return {
+    // state
+    loading,
+    items,
+    hasConfig,
+    // actions
+    ensureConfig,
+    openRecharge,
+  }
+}

+ 5 - 11
app/composables/useTopupPopup.ts

@@ -50,17 +50,11 @@ export interface TopupPopupResult {
 const state = reactive<TopupPopupState>({
   visible: false,
   balance: 0,
-  packages: [
-    { id: 10, diamonds: 10, priceLabel: '10.000' },
-    { id: 200, diamonds: 200, priceLabel: '20.000' },
-    { id: 300, diamonds: 300, priceLabel: '30.000' },
-  ],
-  methods: [
-    { id: 'payermax', name: 'PayerMax' },
-    { id: 'payermax2', name: 'PayerMax 2' },
-  ],
-  selectedPackageId: 10,
-  selectedMethodId: 'payermax',
+  // 实际档位和支付方式由上层(useRecharge)在 open 时注入
+  packages: [],
+  methods: [],
+  selectedPackageId: null,
+  selectedMethodId: null,
 })
 
 const { open: openPaybackResultPopup } = usePaybackResultPopup()

+ 2 - 3
app/pages/mine/index.vue

@@ -20,8 +20,7 @@ definePageMeta({
 })
 
 const router = useRouter()
-const { open: openRechargePopup } = useTopupPopup()
-
+const { openRecharge } = useRecharge()
 const { isPlaymate, userProfile, wallet, refreshUser, logout } = useAuth()
 
 let clipboard: ClipboardJS | null = null
@@ -83,7 +82,7 @@ const handleProfilePage = () => {
 }
 
 const handleRecharge = () => {
-  openRechargePopup()
+  openRecharge()
 }
 
 const handleLogout = async () => {

+ 46 - 0
app/stores/walletRecharge.ts

@@ -0,0 +1,46 @@
+import { defineStore } from 'pinia'
+import type { WalletRechargeConfigItem } from '~/types/api'
+import { walletApi } from '~/api/wallet'
+
+interface WalletRechargeState {
+  /** 充值档位列表(保持与后端字段一致) */
+  items: WalletRechargeConfigItem[]
+  /** 是否正在加载配置 */
+  loading: boolean
+  /** 是否已经初始化过(避免重复请求) */
+  initialized: boolean
+}
+
+export const useWalletRechargeStore = defineStore('walletRecharge', {
+  state: (): WalletRechargeState => ({
+    items: [],
+    loading: false,
+    initialized: false,
+  }),
+
+  actions: {
+    /**
+     * 加载钱包充值配置
+     * 默认只在首次调用时请求,可以通过 force=true 强制刷新
+     */
+    async loadConfig(force = false) {
+      if (this.initialized && !force)
+        return
+
+      this.loading = true
+      try {
+        const res = await walletApi.getRechargeConfig()
+
+        this.items = res?.items ?? []
+        this.initialized = true
+      }
+      catch (error) {
+        // 错误在 http 拦截器已有统一处理,这里仅做兜底日志
+        console.error('Failed to load wallet recharge config:', error)
+      }
+      finally {
+        this.loading = false
+      }
+    },
+  },
+})

+ 2 - 0
app/types/api/index.ts

@@ -3,3 +3,5 @@ export * from './user'
 export * from './category'
 export * from './skill'
 export * from './upload'
+export * from './wallet'
+

+ 25 - 0
app/types/api/wallet.ts

@@ -0,0 +1,25 @@
+// Wallet recharge config
+// 对应后端:/wallet/recharge/browser/config
+
+/**
+ * 单个充值档位
+ * 后端字段:id, diamond, amount
+ */
+export interface WalletRechargeConfigItem {
+  /** ID */
+  id: string
+  /** 钻石数量 */
+  diamond: number
+  /** 金额(数值) */
+  amount: number
+}
+
+/**
+ * 钱包充值配置
+ */
+export interface WalletRechargeConfigVO {
+  /** 充值档位列表 */
+  items: WalletRechargeConfigItem[]
+}
+
+