Ver código fonte

添加会员购买功能,支持选择套餐和支付方式,优化界面样式

wuyi 3 meses atrás
pai
commit
f988397f1e
2 arquivos alterados com 156 adições e 23 exclusões
  1. 2 2
      src/store/user.ts
  2. 154 21
      src/views/Account.vue

+ 2 - 2
src/store/user.ts

@@ -20,7 +20,7 @@ export const useUserStore = defineStore("user", () => {
     const response = await apiLogin(username, password);
     setToken(response.token);
     setUserInfo(response.user);
-    userManuallyLoggedOut.value = false; // 登录成功后清除主动退出标记
+    userManuallyLoggedOut.value = false;
     return response;
   };
 
@@ -40,7 +40,7 @@ export const useUserStore = defineStore("user", () => {
       const response = await newGuest(code);
       setToken(response.token);
       setUserInfo(response.user);
-      userManuallyLoggedOut.value = false; // 创建游客账户时也清除主动退出标记
+      userManuallyLoggedOut.value = false;
       return response;
     } catch (error) {
       console.error("创建游客账号失败", error);

+ 154 - 21
src/views/Account.vue

@@ -11,6 +11,7 @@ const isGuest = computed(() => {
 const emit = defineEmits(["show-login"]);
 
 const showUpgradeDialog = ref(false);
+const showMembershipDialog = ref(false);
 const upgradeForm = ref({
   name: null,
   password: null,
@@ -19,6 +20,20 @@ const upgradeForm = ref({
 });
 const isLoading = ref(false);
 
+// 会员购买选项
+const membershipPlans = [
+  { key: "hourly", label: "一小时", price: "1", duration: "1小时" },
+  { key: "daily", label: "一天", price: "10", duration: "1天" },
+  { key: "weekly", label: "一周", price: "60", duration: "7天" },
+  { key: "monthly", label: "一个月", price: "200", duration: "30天" },
+  { key: "quarterly", label: "三个月", price: "500", duration: "90天" },
+  { key: "yearly", label: "一年", price: "1000", duration: "365天" },
+  { key: "lifetime", label: "终身", price: "1500", duration: "永久" },
+];
+
+const selectedPlan = ref("");
+const selectedPayment = ref("alipay");
+
 const handleLogout = () => {
   userStore.logout();
 };
@@ -27,6 +42,24 @@ const showLoginDialog = () => {
   emit("show-login");
 };
 
+const handleMembershipPurchase = () => {
+  if (!selectedPlan.value) {
+    alert("请选择会员套餐");
+    return;
+  }
+
+  const plan = membershipPlans.find((p) => p.key === selectedPlan.value);
+  console.log("购买会员:", plan, "支付方式:", selectedPayment.value);
+
+  // TODO: 调用支付API
+  alert(
+    `即将跳转到支付页面\n套餐: ${plan?.label}\n价格: ${plan?.price}\n支付方式: 支付宝`
+  );
+
+  showMembershipDialog.value = false;
+  selectedPlan.value = "";
+};
+
 const handleUpgrade = async () => {
   if (!upgradeForm.value.name || !upgradeForm.value.password) {
     alert("请填写用户名和密码");
@@ -109,6 +142,7 @@ const handleUpgrade = async () => {
           成为正式用户
         </button>
         <button
+          @click="showMembershipDialog = true"
           class="px-3 py-1.5 rounded-lg bg-brand text-slate-900 text-sm font-medium"
           :disabled="isGuest"
           :class="{ 'opacity-50 cursor-not-allowed': isGuest }"
@@ -125,18 +159,14 @@ const handleUpgrade = async () => {
       </button>
     </div>
 
-    <div v-if="isLoggedIn" class="grid grid-cols-3 gap-3">
-      <button class="stat">
-        <strong class="text-lg">6</strong>
-        <span>已购</span>
-      </button>
+    <div v-if="isLoggedIn" class="grid grid-cols-2 gap-3">
       <button class="stat">
         <strong class="text-lg">12</strong>
         <span>收藏</span>
       </button>
       <button class="stat">
-        <strong class="text-lg">34</strong>
-        <span>历史</span>
+        <strong class="text-lg">6</strong>
+        <span>已购</span>
       </button>
     </div>
 
@@ -167,56 +197,58 @@ const handleUpgrade = async () => {
       class="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
       @click.self="showUpgradeDialog = false"
     >
-      <div class="bg-white rounded-2xl p-6 w-full max-w-md mx-4">
-        <h3 class="text-xl font-semibold text-gray-900 mb-4">成为正式用户</h3>
+      <div
+        class="bg-surface border border-white/10 rounded-2xl p-6 w-full max-w-md mx-4"
+      >
+        <h3 class="text-xl font-semibold text-white/90 mb-4">成为正式用户</h3>
 
         <form @submit.prevent="handleUpgrade" class="space-y-4">
           <div>
-            <label class="block text-sm font-medium text-gray-700 mb-1">
-              用户名 <span class="text-red-500">*</span>
+            <label class="block text-sm font-medium text-white/70 mb-1">
+              用户名 <span class="text-red-400">*</span>
             </label>
             <input
               v-model="upgradeForm.name"
               type="text"
               required
-              class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
+              class="w-full px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-white placeholder-white/40 focus:ring-2 focus:ring-brand focus:border-transparent"
               placeholder="请输入用户名"
             />
           </div>
 
           <div>
-            <label class="block text-sm font-medium text-gray-700 mb-1">
-              密码 <span class="text-red-500">*</span>
+            <label class="block text-sm font-medium text-white/70 mb-1">
+              密码 <span class="text-red-400">*</span>
             </label>
             <input
               v-model="upgradeForm.password"
               type="password"
               required
-              class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
+              class="w-full px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-white placeholder-white/40 focus:ring-2 focus:ring-brand focus:border-transparent"
               placeholder="请输入密码"
             />
           </div>
 
           <div>
-            <label class="block text-sm font-medium text-gray-700 mb-1">
+            <label class="block text-sm font-medium text-white/70 mb-1">
               邮箱
             </label>
             <input
               v-model="upgradeForm.email"
               type="email"
-              class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
+              class="w-full px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-white placeholder-white/40 focus:ring-2 focus:ring-brand focus:border-transparent"
               placeholder="请输入邮箱(可选)"
             />
           </div>
 
           <div>
-            <label class="block text-sm font-medium text-gray-700 mb-1">
+            <label class="block text-sm font-medium text-white/70 mb-1">
               手机号
             </label>
             <input
               v-model="upgradeForm.phone"
               type="tel"
-              class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
+              class="w-full px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-white placeholder-white/40 focus:ring-2 focus:ring-brand focus:border-transparent"
               placeholder="请输入手机号(可选)"
             />
           </div>
@@ -225,14 +257,14 @@ const handleUpgrade = async () => {
             <button
               type="button"
               @click="showUpgradeDialog = false"
-              class="flex-1 px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition"
+              class="flex-1 px-4 py-2 border border-white/20 text-white/70 rounded-lg hover:bg-white/5 transition"
             >
               取消
             </button>
             <button
               type="submit"
               :disabled="isLoading"
-              class="flex-1 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition disabled:opacity-50"
+              class="flex-1 px-4 py-2 bg-brand text-slate-900 rounded-lg hover:bg-brand/90 transition disabled:opacity-50"
             >
               {{ isLoading ? "处理中..." : "确认升级" }}
             </button>
@@ -240,6 +272,107 @@ const handleUpgrade = async () => {
         </form>
       </div>
     </div>
+
+    <!-- 会员购买弹窗 -->
+    <div
+      v-if="showMembershipDialog"
+      class="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
+      @click.self="showMembershipDialog = false"
+    >
+      <div
+        class="bg-surface border border-white/10 rounded-2xl p-6 w-full max-w-lg mx-4 max-h-[90vh] overflow-y-auto"
+      >
+        <h3 class="text-xl font-semibold text-white/90 mb-6 text-center">
+          开通会员
+        </h3>
+
+        <!-- 会员套餐选择 -->
+        <div class="space-y-2 mb-4">
+          <h4 class="text-sm font-medium text-white/70 mb-2">选择套餐</h4>
+          <div class="grid grid-cols-1 gap-2">
+            <label
+              v-for="plan in membershipPlans"
+              :key="plan.key"
+              class="relative cursor-pointer"
+            >
+              <input
+                v-model="selectedPlan"
+                :value="plan.key"
+                type="radio"
+                name="membership-plan"
+                class="sr-only"
+              />
+              <div
+                class="border-2 rounded-lg p-2 transition-all"
+                :class="
+                  selectedPlan === plan.key
+                    ? 'border-brand bg-brand/10'
+                    : 'border-white/20 hover:border-white/30'
+                "
+              >
+                <div class="flex justify-between items-center">
+                  <div>
+                    <div class="font-medium text-white/90 text-sm">
+                      {{ plan.label }}
+                    </div>
+                    <div class="text-xs text-white/60">{{ plan.duration }}</div>
+                  </div>
+                  <div class="text-right">
+                    <div class="font-semibold text-white/90 text-sm">
+                      {{ plan.price }}
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </label>
+          </div>
+        </div>
+
+        <!-- 支付方式选择 -->
+        <div class="space-y-2 mb-4">
+          <h4 class="text-sm font-medium text-white/70 mb-2">支付方式</h4>
+          <div class="space-y-2">
+            <label
+              class="flex items-center p-2 border border-white/20 rounded-lg cursor-pointer hover:bg-white/5"
+            >
+              <input
+                v-model="selectedPayment"
+                value="alipay"
+                type="radio"
+                name="payment-method"
+                class="sr-only"
+              />
+              <div class="flex items-center">
+                <div
+                  class="w-8 h-8 bg-blue-500 rounded flex items-center justify-center mr-3"
+                >
+                  <span class="text-white text-sm font-bold">支</span>
+                </div>
+                <span class="text-white/90">支付宝</span>
+              </div>
+            </label>
+          </div>
+        </div>
+
+        <!-- 操作按钮 -->
+        <div class="flex gap-3 pt-3 border-t border-white/10">
+          <button
+            type="button"
+            @click="showMembershipDialog = 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="handleMembershipPurchase"
+            :disabled="!selectedPlan"
+            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"
+          >
+            立即购买
+          </button>
+        </div>
+      </div>
+    </div>
   </section>
 </template>
 <style scoped>