fix: 样式修改

This commit is contained in:
2025-07-30 23:55:34 -07:00
parent c1aa738154
commit 816df42bc3
15 changed files with 310 additions and 331 deletions
@@ -0,0 +1,54 @@
import React from 'react';
import { PlanList } from './index';
import { ProcessedPlanData } from './types';
interface TabContentProps {
tabValue: string;
yearlyPlans: ProcessedPlanData[];
monthlyPlans: ProcessedPlanData[];
isLoading: boolean;
error: Error | null;
onRetry: () => void;
onSubscribe: (plan: ProcessedPlanData) => void;
firstPlanCardRef?: React.RefObject<HTMLDivElement | null>;
}
export const TabContent: React.FC<TabContentProps> = ({
tabValue,
yearlyPlans,
monthlyPlans,
isLoading,
error,
onRetry,
onSubscribe,
firstPlanCardRef,
}) => {
return (
<div>
{tabValue === 'year' && (
<PlanList
plans={yearlyPlans}
isLoading={isLoading}
tabValue={tabValue}
error={error}
onRetry={onRetry}
emptyMessage='暂无年付套餐'
onSubscribe={onSubscribe}
firstPlanCardRef={firstPlanCardRef}
/>
)}
{tabValue === 'month' && (
<PlanList
plans={monthlyPlans}
tabValue={tabValue}
isLoading={isLoading}
error={error}
onRetry={onRetry}
emptyMessage='暂无月付套餐'
onSubscribe={onSubscribe}
firstPlanCardRef={firstPlanCardRef}
/>
)}
</div>
);
};
+37 -89
View File
@@ -16,34 +16,8 @@ import {
useState,
} from 'react';
// 定义数据类型
interface SubscriptionData {
id: string;
name: string;
price: number;
originalPrice: number;
duration: string;
unit_price: number;
discount: Array<{
quantity: number;
discount: number;
}>;
features: {
traffic: string;
duration: string;
onlineIPs: string;
connections: string;
bandwidth: string;
nodes: string;
stability: number; // 星级 1-5
};
}
// 处理后的套餐数据类型
interface ProcessedPlanData extends SubscriptionData {
origin_price: string;
discount_price: string;
}
import { TabContent } from './TabContent';
import { ProcessedPlanData } from './types';
// 加载状态组件
const LoadingState = () => (
@@ -150,33 +124,22 @@ const FeatureList = ({ plan }: { plan: ProcessedPlanData }) => {
};
// 套餐卡片组件
const PlanCard = ({
plan,
onSubscribe,
isFirstCard = false, // 新增参数标识是否为第一项
}: {
plan: ProcessedPlanData;
tabValue: string;
onSubscribe?: (plan: ProcessedPlanData) => void;
isFirstCard?: boolean;
}) => {
const cardRef = useRef<HTMLDivElement>(null);
// 如果是第一项,将ref传递给父组件
useEffect(() => {
if (isFirstCard && cardRef.current) {
// 可以通过回调函数将高度传递给父组件
// 或者使用ref转发
}
}, [isFirstCard]);
const PlanCard = forwardRef<
HTMLDivElement,
{
plan: ProcessedPlanData;
tabValue: string;
onSubscribe?: (plan: ProcessedPlanData) => void;
isFirstCard?: boolean;
}
>(({ plan, onSubscribe, isFirstCard = false }, ref) => {
const handleSubscribe = () => {
onSubscribe?.(plan);
};
return (
<div
ref={isFirstCard ? cardRef : undefined}
ref={ref}
className='relative w-full max-w-[345px] cursor-pointer rounded-[20px] border border-[#D9D9D9] bg-white p-4 transition-all duration-300 hover:shadow-lg sm:p-6 md:p-8'
>
{/* 套餐名称 */}
@@ -192,10 +155,12 @@ const PlanCard = ({
<FeatureList plan={plan} />
</div>
);
};
});
PlanCard.displayName = 'PlanCard';
// 套餐列表组件
const PlanList = ({
export const PlanList = ({
plans,
tabValue,
isLoading,
@@ -212,7 +177,7 @@ const PlanList = ({
onRetry: () => void;
emptyMessage: string;
onSubscribe?: (plan: ProcessedPlanData) => void;
firstPlanCardRef?: React.RefObject<HTMLDivElement>;
firstPlanCardRef?: React.RefObject<HTMLDivElement | null>;
}) => {
if (isLoading) return <LoadingState />;
if (error) return <ErrorState onRetry={onRetry} />;
@@ -289,12 +254,12 @@ const OfferDialog = forwardRef<OfferDialogRef>((props, ref) => {
const response = await getSubscription({ skipErrorHandler: true });
// 确保返回有效的数组,避免 undefined
const list = response.data?.data?.list || [];
return list.filter((v) => v.unit_time === 'Month') as unknown as SubscriptionData[];
return list.filter((v) => v.unit_time === 'Month') as unknown as ProcessedPlanData[];
} catch (err) {
// 自定义错误处理
console.error('获取订阅数据失败:', err);
// 返回空数组而不是抛出错误,避免 queryFn 返回 undefined
return [] as SubscriptionData[];
return [] as ProcessedPlanData[];
}
},
enabled: false, // 初始不执行,手动控制
@@ -336,7 +301,7 @@ const OfferDialog = forwardRef<OfferDialogRef>((props, ref) => {
};
// 处理套餐数据的工具函数
const processPlanData = (item: SubscriptionData, isYearly: boolean): ProcessedPlanData => {
const processPlanData = (item: ProcessedPlanData, isYearly: boolean): ProcessedPlanData => {
if (isYearly) {
const discountItem = item.discount?.find((v) => v.quantity === 12);
return {
@@ -377,14 +342,13 @@ const OfferDialog = forwardRef<OfferDialogRef>((props, ref) => {
'right-6 top-6 font-bold text-black opacity-100 focus:ring-0 focus:ring-offset-0'
}
>
<DialogTitle className={''}>
<div className={'text-4xl font-bold text-[#0F2C53] md:mb-4 md:text-center md:text-5xl'}>
</div>
<div className={'text-lg font-medium text-[#666666] md:text-center'}>
</div>
</DialogTitle>
<DialogTitle className={'sr-only'}></DialogTitle>
<div className={'text-4xl font-bold text-[#0F2C53] md:mb-4 md:text-center md:text-5xl'}>
</div>
<div className={'text-lg font-medium text-[#666666] md:text-center'}>
</div>
<div>
<Tabs
defaultValue='year'
@@ -416,32 +380,16 @@ const OfferDialog = forwardRef<OfferDialogRef>((props, ref) => {
className='overflow-y-auto'
style={{ height: `calc(${scrollAreaHeight}px - 32px)` }}
>
<div>
{tabValue === 'year' && (
<PlanList
plans={yearlyPlans}
isLoading={isLoading}
tabValue={tabValue}
error={error}
onRetry={refetch}
emptyMessage='暂无年付套餐'
onSubscribe={handleSubscribe}
firstPlanCardRef={firstPlanCardRef} // 传递ref给第一项
/>
)}
{tabValue === 'month' && (
<PlanList
plans={monthlyPlans}
tabValue={tabValue}
isLoading={isLoading}
error={error}
onRetry={refetch}
emptyMessage='暂无月付套餐'
onSubscribe={handleSubscribe}
firstPlanCardRef={firstPlanCardRef} // 传递ref给第一项
/>
)}
</div>
<TabContent
tabValue={tabValue}
yearlyPlans={yearlyPlans}
monthlyPlans={monthlyPlans}
isLoading={isLoading}
error={error}
onRetry={refetch}
onSubscribe={handleSubscribe}
firstPlanCardRef={firstPlanCardRef}
/>
</ScrollArea>
</div>
</DialogContent>
@@ -0,0 +1,26 @@
export interface SubscriptionData {
id: string;
name: string;
price: number;
originalPrice: number;
duration: string;
unit_price: number;
discount: Array<{
quantity: number;
discount: number;
}>;
features: {
traffic: string;
duration: string;
onlineIPs: string;
connections: string;
bandwidth: string;
nodes: string;
stability: number; // 星级 1-5
};
}
export interface ProcessedPlanData extends SubscriptionData {
origin_price: string;
discount_price: string;
}