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 { 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,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
useRef,
useState,
} from 'react';
import { TabContent } from './TabContent';
import { ProcessedPlanData } from './types';
// 加载状态组件
const LoadingState = () => (
);
// 错误状态组件
const ErrorState = ({ onRetry }: { onRetry: () => void }) => (
);
// 空状态组件
const EmptyState = ({ message }: { message: string }) => (
);
// 价格显示组件
const PriceDisplay = ({ plan }: { plan: ProcessedPlanData }) => (
{plan.origin_price && (
${plan.origin_price}
)}
${plan.discount_price}
/年
{plan.origin_price && (
年付享受8折优惠
)}
);
// 订阅按钮组件
const SubscribeButton = ({ onClick }: { onClick?: () => void }) => (
);
// 星级评分组件
const StarRating = ({ rating, maxRating = 5 }: { rating: number; maxRating?: number }) => (
{Array.from({ length: Math.min(rating, maxRating) }, (_, i) => (
✭
))}
);
// 功能列表组件
const FeatureList = ({ plan }: { plan: ProcessedPlanData }) => {
const features = [
{ label: '可用流量', value: plan.features?.traffic || '1' },
{ label: '套餐时长', value: plan.features?.duration || '1' },
{ label: '在线IP', value: plan.features?.onlineIPs || '2' },
{ label: '在线连接数', value: plan.features?.connections || '3' },
{ label: '峰值带宽', value: plan.features?.bandwidth || '2' },
{ label: '可用节点', value: plan.features?.nodes || '11' },
];
return (
{features.map((feature) => (
-
{feature.label}:
{feature.value}
))}
-
网络稳定指数:
);
};
// 套餐卡片组件
const PlanCard = forwardRef<
HTMLDivElement,
{
plan: ProcessedPlanData;
tabValue: string;
onSubscribe?: (plan: ProcessedPlanData) => void;
isFirstCard?: boolean;
}
>(({ plan, onSubscribe, isFirstCard = false }, ref) => {
const handleSubscribe = () => {
onSubscribe?.(plan);
};
return (
{/* 套餐名称 */}
{plan.name}
{/* 价格区域 */}
{/* 订阅按钮 */}
{/* 功能列表 */}
);
});
PlanCard.displayName = 'PlanCard';
// 套餐列表组件
export const PlanList = ({
plans,
tabValue,
isLoading,
error,
onRetry,
emptyMessage,
onSubscribe,
firstPlanCardRef, // 新增参数
}: {
plans: ProcessedPlanData[];
tabValue: string;
isLoading: boolean;
error: any;
onRetry: () => void;
emptyMessage: string;
onSubscribe?: (plan: ProcessedPlanData) => void;
firstPlanCardRef?: React.RefObject;
}) => {
if (isLoading) return ;
if (error) return ;
if (plans.length === 0) return ;
return (
{plans.map((plan, index) => (
))}
);
};
export interface OfferDialogRef {
show: () => void;
hide: () => void;
}
const OfferDialog = forwardRef((props, ref) => {
const [open, setOpen] = useState(false);
const [tabValue, setTabValue] = useState('year');
const [selectedPlan, setSelectedPlan] = useState(null);
const [scrollAreaHeight, setScrollAreaHeight] = useState(450);
const [planCardHeight, setPlanCardHeight] = useState(600);
const dialogRef = useRef(null);
const scrollAreaRef = useRef(null);
const firstPlanCardRef = useRef(null);
// 获取PlanCard高度
const getPlanCardHeight = useCallback(() => {
if (firstPlanCardRef.current) {
return firstPlanCardRef.current.offsetHeight;
}
return 600; // 默认高度
}, []);
// 计算 ScrollArea 高度
const calculateScrollAreaHeight = useCallback(() => {
if (dialogRef.current && scrollAreaRef.current) {
const isMobile = window.innerWidth < 768;
if (isMobile) {
// 移动端:使用动态计算
const dialogHeight = dialogRef.current.offsetHeight;
const scrollAreaTop = scrollAreaRef.current.offsetTop;
const calculatedHeight = dialogHeight - scrollAreaTop;
setScrollAreaHeight(calculatedHeight);
} else {
// PC端:使用PlanCard第一项高度
const cardHeight = getPlanCardHeight();
setScrollAreaHeight(cardHeight);
}
}
}, [getPlanCardHeight]);
// 使用 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.filter((v) => v.unit_time === 'Month') as unknown as ProcessedPlanData[];
} catch (err) {
// 自定义错误处理
console.error('获取订阅数据失败:', err);
// 返回空数组而不是抛出错误,避免 queryFn 返回 undefined
return [] as ProcessedPlanData[];
}
},
enabled: false, // 初始不执行,手动控制
retry: 1, // 失败时重试1次
});
// 监听对话框打开
useEffect(() => {
if (open) {
// 等待 DOM 渲染完成
const timer = setTimeout(calculateScrollAreaHeight, 0);
return () => clearTimeout(timer);
}
}, [open, calculateScrollAreaHeight]);
// 监听窗口大小变化
useEffect(() => {
if (open) {
refetch(); // 对话框打开时重新获取数据
const handleResize = () => {
calculateScrollAreaHeight();
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}
}, [open, calculateScrollAreaHeight]);
useImperativeHandle(ref, () => ({
show: () => setOpen(true),
hide: () => setOpen(false),
}));
// 处理订阅点击
const handleSubscribe = (plan: ProcessedPlanData) => {
setSelectedPlan(plan);
console.log('用户选择了套餐:', plan);
// 这里可以添加订阅逻辑,比如跳转到支付页面或显示确认对话框
};
// 处理套餐数据的工具函数
const processPlanData = (item: ProcessedPlanData, isYearly: boolean): ProcessedPlanData => {
if (isYearly) {
const discountItem = item.discount?.find((v) => v.quantity === 12);
return {
...item,
origin_price: unitConversion('centsToDollars', item.unit_price * 12).toString(), // 原价
discount_price: unitConversion(
'centsToDollars',
item.unit_price * ((discountItem?.discount || 100) / 100) * 12,
).toString(), // 优惠价格
};
} else {
return {
...item,
origin_price: '', // 月付没有原价
discount_price: unitConversion('centsToDollars', item.unit_price).toString(), // 月付价格
};
}
};
// 使用 useMemo 优化数据处理性能
const yearlyPlans: ProcessedPlanData[] = useMemo(
() => data.map((item) => processPlanData(item, true)),
[data],
);
const monthlyPlans: ProcessedPlanData[] = useMemo(
() => data.map((item) => processPlanData(item, false)),
[data],
);
return (
);
});
export default OfferDialog;