Kaynağa Gözat

新增价格配置接口和价格管理功能,优化账户和视频播放器页面逻辑,提升用户体验

wuyi 3 ay önce
ebeveyn
işleme
c5802e1a3b
5 değiştirilmiş dosya ile 195 ekleme ve 22 silme
  1. 6 0
      src/services/api.ts
  2. 105 0
      src/store/price.ts
  3. 63 0
      src/types/price.ts
  4. 9 11
      src/views/Account.vue
  5. 12 11
      src/views/VideoPlayer.vue

+ 6 - 0
src/services/api.ts

@@ -181,6 +181,12 @@ export const getSinglePurchaseList = async (
   return res.data;
 };
 
+// 获取价格配置
+export const getPriceConfig = async (): Promise<any> => {
+  const res = await api.get("/config/team/user");
+  return res.data;
+};
+
 /**
  * ===================== 视频相关接口 =====================
  */

+ 105 - 0
src/store/price.ts

@@ -0,0 +1,105 @@
+import { defineStore } from "pinia";
+import { ref, computed } from "vue";
+import { getPriceConfig } from "@/services/api";
+import {
+  PriceConfigResponse,
+  PriceConfigMap,
+  MembershipPlan,
+  MembershipType,
+  membershipLabels,
+  membershipDurations,
+} from "@/types/price";
+
+export const usePriceStore = defineStore("price", () => {
+  // 价格配置数据
+  const priceConfig = ref<PriceConfigMap | null>(null);
+
+  // 加载状态
+  const isLoading = ref(false);
+
+  // 错误信息
+  const error = ref<string | null>(null);
+
+  // 获取价格配置
+  const fetchPriceConfig = async () => {
+    if (isLoading.value) return;
+
+    isLoading.value = true;
+    error.value = null;
+
+    try {
+      const response: PriceConfigResponse = await getPriceConfig();
+
+      // 将数组转换为映射对象
+      const configMap: PriceConfigMap = {
+        hourly: "0",
+        daily: "0",
+        weekly: "0",
+        monthly: "0",
+        quarterly: "0",
+        yearly: "0",
+        lifetime: "0",
+        single: "0",
+      };
+
+      response.forEach((item) => {
+        if (item.name in configMap) {
+          configMap[item.name as keyof PriceConfigMap] = item.value;
+        }
+      });
+
+      priceConfig.value = configMap;
+    } catch (err) {
+      console.error("获取价格配置失败:", err);
+      error.value = "获取价格配置失败";
+    } finally {
+      isLoading.value = false;
+    }
+  };
+
+  // 获取指定类型的价格
+  const getPrice = (type: MembershipType): string => {
+    return priceConfig.value?.[type] || "0";
+  };
+
+  // 获取单片购买价格
+  const getSinglePrice = (): string => {
+    return getPrice("single");
+  };
+
+  // 获取会员套餐列表
+  const getMembershipPlans = computed((): MembershipPlan[] => {
+    if (!priceConfig.value) return [];
+
+    const membershipTypes: MembershipType[] = [
+      "hourly",
+      "daily",
+      "weekly",
+      "monthly",
+      "quarterly",
+      "yearly",
+      "lifetime",
+    ];
+
+    return membershipTypes.map((type) => ({
+      key: type,
+      label: membershipLabels[type],
+      price: priceConfig.value![type],
+      duration: membershipDurations[type],
+    }));
+  });
+
+  // 检查是否已加载价格配置
+  const isPriceConfigLoaded = computed(() => priceConfig.value !== null);
+
+  return {
+    priceConfig,
+    isLoading,
+    error,
+    fetchPriceConfig,
+    getPrice,
+    getSinglePrice,
+    getMembershipPlans,
+    isPriceConfigLoaded,
+  };
+});

+ 63 - 0
src/types/price.ts

@@ -0,0 +1,63 @@
+// 价格配置项
+export interface PriceConfigItem {
+  name: string;
+  value: string;
+}
+
+// 价格配置响应
+export type PriceConfigResponse = PriceConfigItem[];
+
+// 会员套餐类型
+export type MembershipType =
+  | "hourly"
+  | "daily"
+  | "weekly"
+  | "monthly"
+  | "quarterly"
+  | "yearly"
+  | "lifetime"
+  | "single";
+
+// 会员套餐信息
+export interface MembershipPlan {
+  key: MembershipType;
+  label: string;
+  price: string;
+  duration: string;
+}
+
+// 价格配置映射
+export interface PriceConfigMap {
+  hourly: string;
+  daily: string;
+  weekly: string;
+  monthly: string;
+  quarterly: string;
+  yearly: string;
+  lifetime: string;
+  single: string;
+}
+
+// 会员套餐标签映射
+export const membershipLabels: Record<MembershipType, string> = {
+  hourly: "一小时",
+  daily: "一天",
+  weekly: "一周",
+  monthly: "一个月",
+  quarterly: "三个月",
+  yearly: "一年",
+  lifetime: "终身",
+  single: "单片购买",
+};
+
+// 会员套餐时长映射
+export const membershipDurations: Record<MembershipType, string> = {
+  hourly: "1小时",
+  daily: "1天",
+  weekly: "7天",
+  monthly: "30天",
+  quarterly: "90天",
+  yearly: "365天",
+  lifetime: "永久",
+  single: "永久",
+};

+ 9 - 11
src/views/Account.vue

@@ -1,10 +1,12 @@
 <script setup lang="ts">
 import { useUserStore } from "@/store/user";
+import { usePriceStore } from "@/store/price";
 import { computed, ref, onMounted } from "vue";
 import { upgradeGuest, purchaseMember, userQueryOrder } from "@/services/api";
 import { vipLevelToText, VipLevel } from "@/types/vip";
 
 const userStore = useUserStore();
+const priceStore = usePriceStore();
 const isLoggedIn = computed(() => !!userStore.token);
 const isGuest = computed(() => {
   return userStore.userInfo?.vipLevel === "guest";
@@ -41,16 +43,8 @@ const upgradeForm = ref({
 const isLoading = ref(false);
 const isPaymentLoading = 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 membershipPlans = computed(() => priceStore.getMembershipPlans);
 
 const selectedPlan = ref("");
 const selectedPayment = ref("alipay");
@@ -222,8 +216,12 @@ const handleQueryOrder = async () => {
   }
 };
 
-onMounted(() => {
+onMounted(async () => {
   checkPaymentSuccess();
+  // 加载价格配置
+  if (!priceStore.isPriceConfigLoaded) {
+    await priceStore.fetchPriceConfig();
+  }
 });
 </script>
 

+ 12 - 11
src/views/VideoPlayer.vue

@@ -156,10 +156,13 @@
         class="bg-surface border border-white/10 rounded-2xl p-6 w-full max-w-sm mx-4 text-center"
       >
         <h3 class="text-lg font-semibold text-white/90 mb-2">确认购买</h3>
-        <p class="text-sm text-white/70 mb-6">
+        <p class="text-sm text-white/70 mb-4">
           确定要购买当前视频吗?<br />
           购买后即可观看完整内容
         </p>
+        <div class="text-lg font-semibold text-brand mb-6">
+          ¥{{ priceStore.getSinglePrice() }}
+        </div>
 
         <div class="space-y-3">
           <button
@@ -607,6 +610,7 @@ import {
 } from "@/services/api";
 import VideoProcessor from "@/components/VideoProcessor.vue";
 import { useUserStore } from "@/store/user";
+import { usePriceStore } from "@/store/price";
 import { VipLevel, canWatchFullVideo, getTrialDuration } from "@/types/vip";
 
 // 路由相关
@@ -615,6 +619,7 @@ const router = useRouter();
 
 // 用户状态
 const userStore = useUserStore();
+const priceStore = usePriceStore();
 
 // 视频播放器引用(现在由 VideoProcessor 组件管理)
 const videoPlayer = ref<HTMLVideoElement>();
@@ -646,16 +651,8 @@ const isTrialMode = ref(false);
 // 单片购买状态
 const isSinglePurchased = 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 membershipPlans = computed(() => priceStore.getMembershipPlans);
 
 const selectedPlan = ref("");
 const selectedPayment = ref("alipay");
@@ -1170,6 +1167,10 @@ watch(
 
 onMounted(async () => {
   // 路由监听器已经在处理所有情况,这里不需要额外逻辑
+  // 加载价格配置
+  if (!priceStore.isPriceConfigLoaded) {
+    await priceStore.fetchPriceConfig();
+  }
 });
 
 onUnmounted(() => {