Przeglądaj źródła

实现会员购买功能,添加支付等待和错误提示弹窗,优化购买流程

wuyi 3 miesięcy temu
rodzic
commit
9b97e2c28b
2 zmienionych plików z 136 dodań i 12 usunięć
  1. 11 0
      src/services/api.ts
  2. 125 12
      src/views/Account.vue

+ 11 - 0
src/services/api.ts

@@ -80,4 +80,15 @@ export const upgradeGuest = async (
   return response.data;
 };
 
+export const purchaseMember = async (
+  userId: number,
+  type: string
+): Promise<any> => {
+  const response = await api.post("/payment/create", {
+    userId,
+    type,
+  });
+  return response.data;
+};
+
 export default api;

+ 125 - 12
src/views/Account.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import { useUserStore } from "@/store/user";
 import { computed, ref } from "vue";
-import { upgradeGuest } from "@/services/api";
+import { upgradeGuest, purchaseMember } from "@/services/api";
 
 const userStore = useUserStore();
 const isLoggedIn = computed(() => !!userStore.token);
@@ -12,6 +12,9 @@ const emit = defineEmits(["show-login"]);
 
 const showUpgradeDialog = ref(false);
 const showMembershipDialog = ref(false);
+const showPaymentWaitingDialog = ref(false);
+const showErrorDialog = ref(false);
+const errorMessage = ref("");
 const upgradeForm = ref({
   name: null,
   password: null,
@@ -19,6 +22,7 @@ const upgradeForm = ref({
   phone: undefined,
 });
 const isLoading = ref(false);
+const isPaymentLoading = ref(false);
 
 // 会员购买选项
 const membershipPlans = [
@@ -42,22 +46,45 @@ const showLoginDialog = () => {
   emit("show-login");
 };
 
-const handleMembershipPurchase = () => {
+const showError = (message: string) => {
+  errorMessage.value = message;
+  showErrorDialog.value = true;
+};
+
+const handleMembershipPurchase = async () => {
   if (!selectedPlan.value) {
     alert("请选择会员套餐");
     return;
   }
 
-  const plan = membershipPlans.find((p) => p.key === selectedPlan.value);
-  console.log("购买会员:", plan, "支付方式:", selectedPayment.value);
+  isPaymentLoading.value = true;
+  try {
+    const response = await purchaseMember(
+      userStore.userInfo.id,
+      selectedPlan.value
+    );
+
+    if (response.code === 1) {
+      // 关闭购买弹窗
+      showMembershipDialog.value = false;
+
+      // 打开支付页面
+      window.open(response.code_url, "_blank");
 
-  // TODO: 调用支付API
-  alert(
-    `即将跳转到支付页面\n套餐: ${plan?.label}\n价格: ${plan?.price}\n支付方式: 支付宝`
-  );
+      // 显示支付等待弹窗
+      showPaymentWaitingDialog.value = true;
 
-  showMembershipDialog.value = false;
-  selectedPlan.value = "";
+      // 重置选择
+      selectedPlan.value = "";
+    } else {
+      showError(`支付失败: ${response.msg || "未知错误"}`);
+    }
+  } catch (error) {
+    console.error("购买会员失败", error);
+    showError("购买失败,请重试");
+  } finally {
+    isPaymentLoading.value = false;
+  }
 };
 
 const handleUpgrade = async () => {
@@ -365,14 +392,100 @@ const handleUpgrade = async () => {
           </button>
           <button
             @click="handleMembershipPurchase"
-            :disabled="!selectedPlan"
+            :disabled="!selectedPlan || isPaymentLoading"
             class="flex-1 px-4 py-2 bg-brand text-slate-900 rounded-lg hover:bg-brand/90 transition disabled:opacity-50 disabled:cursor-not-allowed"
           >
-            立即购买
+            {{ isPaymentLoading ? "处理中..." : "立即购买" }}
+          </button>
+        </div>
+      </div>
+    </div>
+
+    <!-- 支付等待弹窗 -->
+    <div
+      v-if="showPaymentWaitingDialog"
+      class="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
+    >
+      <div
+        class="bg-surface border border-white/10 rounded-2xl p-8 w-full max-w-sm mx-4 text-center"
+      >
+        <!-- 加载动画 -->
+        <div class="mb-6">
+          <div
+            class="w-16 h-16 mx-auto border-4 border-white/20 border-t-brand rounded-full animate-spin"
+          ></div>
+        </div>
+
+        <!-- 等待文字 -->
+        <h3 class="text-lg font-semibold text-white/90 mb-2">等待支付...</h3>
+        <p class="text-sm text-white/60 mb-6">
+          请在支付页面完成支付<br />
+          支付完成后会自动跳转
+        </p>
+
+        <!-- 操作按钮 -->
+        <div class="flex gap-3">
+          <button
+            @click="showPaymentWaitingDialog = false"
+            class="flex-1 px-4 py-2 border border-white/20 text-white/70 rounded-lg hover:bg-white/5 transition"
+          >
+            取消支付
+          </button>
+          <button
+            @click="showPaymentWaitingDialog = false"
+            class="flex-1 px-4 py-2 bg-brand text-slate-900 rounded-lg hover:bg-brand/90 transition"
+          >
+            已完成支付
           </button>
         </div>
       </div>
     </div>
+
+    <!-- 错误提示弹窗 -->
+    <div
+      v-if="showErrorDialog"
+      class="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
+      @click.self="showErrorDialog = false"
+    >
+      <div
+        class="bg-surface border border-white/10 rounded-2xl p-6 w-full max-w-sm mx-4 text-center"
+      >
+        <!-- 错误图标 -->
+        <div class="mb-4">
+          <div
+            class="w-12 h-12 mx-auto bg-red-500/20 rounded-full flex items-center justify-center"
+          >
+            <svg
+              class="w-6 h-6 text-red-400"
+              fill="none"
+              stroke="currentColor"
+              viewBox="0 0 24 24"
+            >
+              <path
+                stroke-linecap="round"
+                stroke-linejoin="round"
+                stroke-width="2"
+                d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
+              />
+            </svg>
+          </div>
+        </div>
+
+        <!-- 错误信息 -->
+        <h3 class="text-lg font-semibold text-white/90 mb-2">操作失败</h3>
+        <p class="text-sm text-white/70 mb-6">
+          {{ errorMessage }}
+        </p>
+
+        <!-- 确认按钮 -->
+        <button
+          @click="showErrorDialog = false"
+          class="w-full px-4 py-2 bg-brand text-slate-900 rounded-lg hover:bg-brand/90 transition"
+        >
+          确定
+        </button>
+      </div>
+    </div>
   </section>
 </template>
 <style scoped>