diff --git a/apps/user/.env.template b/apps/user/.env.template
index 8b81c94..8659c35 100644
--- a/apps/user/.env.template
+++ b/apps/user/.env.template
@@ -3,7 +3,7 @@ NEXT_PUBLIC_DEFAULT_LANGUAGE=en-US
# Site URL and API URL
NEXT_PUBLIC_SITE_URL=https://user.ppanel.dev
-NEXT_PUBLIC_API_URL=https://api.ppanel.dev
+NEXT_PUBLIC_API_URL=https://api.kxsw.us
NEXT_PUBLIC_CDN_URL=https://cdn.jsdelivr.net
# Home Page Settings
diff --git a/apps/user/components/main/OfferDialog/index.tsx b/apps/user/components/main/OfferDialog/index.tsx
index f5fb3d6..7cba533 100644
--- a/apps/user/components/main/OfferDialog/index.tsx
+++ b/apps/user/components/main/OfferDialog/index.tsx
@@ -2,10 +2,11 @@ import CloseSvg from '@/components/CustomIcon/icons/close.svg';
import { getSubscription } from '@/services/user/portal';
import { useQuery } from '@tanstack/react-query';
import { Dialog, DialogContent, DialogTitle } from '@workspace/ui/components/dialog';
-import { Tabs, TabsContent, TabsList, TabsTrigger } from '@workspace/ui/components/tabs';
+import { ScrollArea } from '@workspace/ui/components/scroll-area';
+import { Tabs, TabsList, TabsTrigger } from '@workspace/ui/components/tabs';
+import { unitConversion } from '@workspace/ui/utils';
import Image from 'next/image';
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
-
// 定义数据类型
interface SubscriptionData {
id: string;
@@ -24,15 +25,27 @@ interface SubscriptionData {
};
}
+// 计算折扣价格的函数
+const calculateDiscountPrice = (unitPrice: number, discountPercent: number) => {
+ // 折扣百分比转换为小数 (80 -> 0.8)
+ const discountRate = discountPercent / 100;
+ // 计算折扣后的价格
+ const discountedPrice = unitPrice * discountRate;
+ // 使用 unitConversion 避免浮点数精度问题
+ return unitConversion('dollarsToCents', discountedPrice);
+};
+
+// 计算原价(未折扣价格)
+const calculateOriginalPrice = (unitPrice: number, discountPercent: number) => {
+ // 原价就是 unitPrice
+ return unitConversion('dollarsToCents', unitPrice);
+};
+
// 套餐卡片组件
-const PlanCard = ({ plan, isSelected }: { plan: SubscriptionData; isSelected?: boolean }) => {
+const PlanCard = ({ plan }: { plan: SubscriptionData }) => {
return (
{/* 套餐名称 */}
{plan.name}
@@ -40,14 +53,16 @@ const PlanCard = ({ plan, isSelected }: { plan: SubscriptionData; isSelected?: b
{/* 价格区域 */}
-
- ¥{plan.originalPrice}
-
+ {plan.origin_price && (
+
+ ${plan.origin_price}
+
+ )}
- ${plan.price}
+ ${plan.discount_price}
- /月
+ /年
年付享受8折优惠
@@ -67,7 +82,7 @@ const PlanCard = ({ plan, isSelected }: { plan: SubscriptionData; isSelected?: b
可用流量:
- {plan.features.traffic}
+ 1
@@ -75,7 +90,7 @@ const PlanCard = ({ plan, isSelected }: { plan: SubscriptionData; isSelected?: b
套餐时长:
- {plan.features.duration}
+ 1
@@ -83,7 +98,7 @@ const PlanCard = ({ plan, isSelected }: { plan: SubscriptionData; isSelected?: b
在线IP:
- {plan.features.onlineIPs}
+ 2
@@ -91,7 +106,7 @@ const PlanCard = ({ plan, isSelected }: { plan: SubscriptionData; isSelected?: b
在线连接数:
- {plan.features.connections}
+ 3
@@ -99,7 +114,7 @@ const PlanCard = ({ plan, isSelected }: { plan: SubscriptionData; isSelected?: b
峰值带宽:
- {plan.features.bandwidth}
+ 2
@@ -107,7 +122,7 @@ const PlanCard = ({ plan, isSelected }: { plan: SubscriptionData; isSelected?: b
可用节点:
- {plan.features.nodes}
+ 11
@@ -115,7 +130,7 @@ const PlanCard = ({ plan, isSelected }: { plan: SubscriptionData; isSelected?: b
网络稳定指数:
- {Array.from({ length: plan.features.stability }, (_, i) => (
+ {Array.from({ length: 4 }, (_, i) => (
✭
@@ -136,14 +151,19 @@ const OfferDialog = forwardRef
((props, ref) => {
const [open, setOpen] = useState(false);
// 使用 useQuery 来管理请求
- const { data, isLoading, error, refetch } = useQuery({
+ const {
+ data = [],
+ isLoading,
+ error,
+ refetch,
+ } = useQuery({
queryKey: ['subscription'],
queryFn: async () => {
try {
const response = await getSubscription({ skipErrorHandler: true });
// 确保返回有效的数组,避免 undefined
const list = response.data?.data?.list || [];
- return list as unknown as SubscriptionData[];
+ return list.filter((v) => v.unit_time === 'Month') as unknown as SubscriptionData[];
} catch (err) {
// 自定义错误处理
console.error('获取订阅数据失败:', err);
@@ -167,73 +187,27 @@ const OfferDialog = forwardRef((props, ref) => {
hide: () => setOpen(false),
}));
- // 处理数据
- const processedData =
- data?.map((item) => ({
- ...item,
- displayPrice: `¥${item.price}`,
- displayDuration: item.duration === 'year' ? '年付' : '月付',
- })) || [];
-
- // 如果没有数据,使用模拟数据
- const mockData: SubscriptionData[] = [
- {
- id: '1',
- name: 'Basic Plan',
- price: 8,
- originalPrice: 10,
- duration: 'year',
- features: {
- traffic: '140G',
- duration: '30天',
- onlineIPs: '3个',
- connections: '300',
- bandwidth: '200Mbps',
- nodes: '15个',
- stability: 3,
- },
- },
- {
- id: '2',
- name: 'Standard Plan',
- price: 24,
- originalPrice: 30,
- duration: 'year',
- features: {
- traffic: '160G',
- duration: '30天',
- onlineIPs: '3个',
- connections: '300',
- bandwidth: '300Mbps',
- nodes: '15个',
- stability: 4,
- },
- },
- {
- id: '3',
- name: 'Pro Plan',
- price: 48,
- originalPrice: 60,
- duration: 'year',
- features: {
- traffic: '180G',
- duration: '30天',
- onlineIPs: '3个',
- connections: '300',
- bandwidth: '500Mbps',
- nodes: '29个',
- stability: 5,
- },
- },
- ];
-
- // 使用真实数据或模拟数据
- const displayData = mockData;
-
// 按类型分组数据
- const yearlyPlans = displayData.filter((item) => item.duration === 'year');
- const monthlyPlans = displayData.filter((item) => item.duration === 'month');
- console.log(processedData);
+ const yearlyPlans = data.map((item) => {
+ const discountItem = item.discount.find((v) => v.quantity === 12);
+ return {
+ ...item,
+ origin_price: unitConversion('centsToDollars', item.unit_price * 12), // 原价
+ discount_price: unitConversion(
+ 'centsToDollars',
+ item.unit_price * (discountItem.discount / 100) * 12,
+ ), // 优惠价格
+ };
+ });
+ const monthlyPlans = data.map((item) => {
+ return {
+ ...item,
+ origin_price: '', // 原价
+ discount_price: unitConversion('centsToDollars', item.unit_price), // 优惠价格
+ };
+ });
+ const [tabValue, setTabValue] = useState('year');
+
return (