feat: 修改样式
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import { AiroButton, buttonVariants } from '@workspace/airo-ui/components/AiroButton';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogPortal,
|
||||
AlertDialogTitle,
|
||||
} from '@workspace/airo-ui/components/alert-dialog';
|
||||
import CloseSvg from '@workspace/airo-ui/components/close.svg';
|
||||
import { useImperativeHandle, useState } from 'react';
|
||||
|
||||
// Defining the AlertDialogComponent with internal state and onShow prop
|
||||
const AlertDialogComponent = ({
|
||||
ref,
|
||||
title,
|
||||
description,
|
||||
cancelText = 'Cancel',
|
||||
confirmText = 'Confirm',
|
||||
onConfirm,
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
function show() {
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
show,
|
||||
}));
|
||||
return (
|
||||
<AlertDialog open={open} onOpenChange={setOpen}>
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogContent className={'py-[30px] sm:rounded-[25px]'}>
|
||||
<div className={'absolute right-4 top-6'} onClick={() => setOpen(false)}>
|
||||
<CloseSvg />
|
||||
</div>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className={'text-[#E22C2E]'}>{title}</AlertDialogTitle>
|
||||
<AlertDialogDescription className={'text-base font-light'}>
|
||||
{description}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter className={'sm:justify-center'}>
|
||||
<AlertDialogAction asChild className={buttonVariants({ variant: 'primary' })}>
|
||||
<AiroButton variant={'primary'} onClick={onConfirm}>
|
||||
{confirmText}
|
||||
</AiroButton>
|
||||
</AlertDialogAction>
|
||||
<AlertDialogCancel asChild className={buttonVariants({ variant: 'danger' })}>
|
||||
<AiroButton variant={'danger'}>{cancelText}</AiroButton>
|
||||
</AlertDialogCancel>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogPortal>
|
||||
</AlertDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AlertDialogComponent;
|
||||
@@ -182,14 +182,17 @@ export default function Affiliate() {
|
||||
const MONTHLY_PRICE = proPlan?.unit_price;
|
||||
const discountItem = proPlan?.discount?.find((v) => v.quantity === 12);
|
||||
const YEARLY_PRICE = proPlan?.unit_price * ((discountItem?.discount || 100) / 100) * 12;
|
||||
|
||||
const {
|
||||
common: { invite },
|
||||
} = useGlobalStore();
|
||||
const [count, setCount] = useState<number>(10);
|
||||
const clamp = (n: number) => Math.max(0, Math.min(10000, Math.floor(n)));
|
||||
|
||||
const firstMonth = count * MONTHLY_PRICE * 0.5; // 50%
|
||||
const firstYear = count * YEARLY_PRICE * 0.3; // 30%
|
||||
const recurMonth = count * MONTHLY_PRICE * 0.2; // 20%
|
||||
const recurYear = count * YEARLY_PRICE * 0.2; // 20%
|
||||
const firstMonth = count * MONTHLY_PRICE * (invite.first_purchase_percentage / 100); // 50%
|
||||
const firstYear =
|
||||
count * YEARLY_PRICE * (invite.first_yearly_purchase_percentage / 100); // 30%
|
||||
const recurMonth = count * MONTHLY_PRICE * (invite.non_first_purchase_percentage / 100); // 20%
|
||||
const recurYear = count * YEARLY_PRICE * (invite.non_first_purchase_percentage / 100); // 20%
|
||||
|
||||
return (
|
||||
<div className='space-y-4'>
|
||||
|
||||
@@ -78,36 +78,12 @@ const PriceDisplay = ({ plan }: { plan: ProcessedPlanData }) => {
|
||||
|
||||
import { useLoginDialog } from '@/app/auth/LoginDialogContext';
|
||||
import { Display } from '@/components/display';
|
||||
import Modal from '@/components/Modal';
|
||||
import Purchase from '@/components/subscribe/purchase';
|
||||
import useGlobalStore from '@/config/use-global';
|
||||
import { queryUserSubscribe } from '@/services/user/user';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
// 订阅按钮组件
|
||||
const SubscribeButton = ({ onClick }: { onClick?: () => void }) => {
|
||||
const { user } = useGlobalStore();
|
||||
const { openLoginDialog } = useLoginDialog();
|
||||
const t = useTranslations('components.offerDialog');
|
||||
|
||||
function handleClick() {
|
||||
console.log('click', user);
|
||||
if (!user) {
|
||||
// 强制登陆
|
||||
openLoginDialog(false);
|
||||
return;
|
||||
}
|
||||
onClick();
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleClick}
|
||||
className='h-10 w-full rounded-full bg-[#0F2C53] text-sm font-medium text-white shadow-md transition-all duration-300 hover:bg-[#225BA9] sm:h-10 sm:text-sm md:h-[40px] md:text-[14px]'
|
||||
>
|
||||
{t('subscribe')}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
// 星级评分组件
|
||||
const StarRating = ({ rating, maxRating = 5 }: { rating: number; maxRating?: number }) => (
|
||||
<div className='text-right text-xs font-light leading-[1.8461538461538463em] text-black sm:text-[13px]'>
|
||||
@@ -186,9 +162,26 @@ const PlanCard = forwardRef<
|
||||
isFirstCard?: boolean;
|
||||
}
|
||||
>(({ plan, onSubscribe, isFirstCard = false }, ref) => {
|
||||
const handleSubscribe = () => {
|
||||
const { user } = useGlobalStore();
|
||||
const { openLoginDialog } = useLoginDialog();
|
||||
const t = useTranslations('components.offerDialog');
|
||||
const ModalRef = useRef(null);
|
||||
async function handleSubscribe() {
|
||||
if (!user) {
|
||||
// 强制登陆
|
||||
openLoginDialog(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 有生效套餐进行弹窗提示
|
||||
const { data } = await queryUserSubscribe();
|
||||
if (data?.data?.list?.[0].status === 1) {
|
||||
ModalRef.current.show();
|
||||
return;
|
||||
}
|
||||
|
||||
onSubscribe?.(plan);
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -202,10 +195,26 @@ const PlanCard = forwardRef<
|
||||
<PriceDisplay plan={plan} />
|
||||
|
||||
{/* 订阅按钮 */}
|
||||
<SubscribeButton onClick={handleSubscribe} />
|
||||
<button
|
||||
onClick={handleSubscribe}
|
||||
className='h-10 w-full rounded-full bg-[#0F2C53] text-sm font-medium text-white shadow-md transition-all duration-300 hover:bg-[#225BA9] sm:h-10 sm:text-sm md:h-[40px] md:text-[14px]'
|
||||
>
|
||||
{t('subscribe')}
|
||||
</button>
|
||||
|
||||
{/* 功能列表 */}
|
||||
<FeatureList plan={plan} />
|
||||
|
||||
<Modal
|
||||
ref={ModalRef}
|
||||
title={'【重要提示】'}
|
||||
description={
|
||||
'您已有正在生效的套餐,如继续购买新的套餐,原套餐将自动失效。账户套餐将自动转为最新套餐。未使用部分不支持退款或叠加。'
|
||||
}
|
||||
confirmText={'同意'}
|
||||
cancelText={'取消'}
|
||||
onConfirm={() => onSubscribe?.(plan)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -350,7 +359,6 @@ const OfferDialog = forwardRef<OfferDialogRef>((props, ref) => {
|
||||
// 处理订阅点击
|
||||
const handleSubscribe = (plan: ProcessedPlanData) => {
|
||||
setSelectedPlan(plan);
|
||||
console.log('用户选择了套餐:', plan);
|
||||
// 这里可以添加订阅逻辑,比如跳转到支付页面或显示确认对话框
|
||||
PurchaseRef.current.show(plan, tabValue);
|
||||
};
|
||||
@@ -412,12 +420,6 @@ const OfferDialog = forwardRef<OfferDialogRef>((props, ref) => {
|
||||
<span className='absolute -top-8 left-16 z-10 rounded-md bg-[#E22C2E] px-2 py-0.5 text-[10px] font-bold leading-none text-white shadow sm:text-xs'>
|
||||
-20%
|
||||
{/* 小三角箭头 */}
|
||||
{/* <span className="
|
||||
absolute right-0 top-full
|
||||
block h-0 w-0
|
||||
border-l-[6px] border-r-[6px] border-t-[16px]
|
||||
border-l-transparent border-r-transparent border-t-[#E22C2E]
|
||||
" />*/}
|
||||
<span
|
||||
className='absolute right-0 top-[80%] h-10 w-2 bg-[#E22C2E]'
|
||||
style={{ clipPath: 'polygon(100% 0, 100% 100%, 0 0)' }}
|
||||
|
||||
@@ -109,11 +109,11 @@ const Purchase = forwardRef<PurchaseDialogRef, PurchaseProps>((props, ref) => {
|
||||
const response = await purchase(params as API.PurchaseOrderRequest);
|
||||
const orderNo = response.data.data?.order_no;
|
||||
if (orderNo) {
|
||||
await getUserInfo();
|
||||
const data = await purchaseCheckout({
|
||||
orderNo: orderNo,
|
||||
returnUrl: window.location.href,
|
||||
});
|
||||
await getUserInfo();
|
||||
if (data.data?.type === 'url' && data.data.checkout_url) {
|
||||
window.open(data.data.checkout_url, '_blank');
|
||||
} else {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import useGlobalStore from '@/config/use-global';
|
||||
import { recharge } from '@/services/user/order';
|
||||
import { AiroButton } from '@workspace/airo-ui/components/AiroButton';
|
||||
import { Button, ButtonProps } from '@workspace/airo-ui/components/button';
|
||||
import { ButtonProps } from '@workspace/airo-ui/components/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -43,13 +43,13 @@ export default function Recharge(props: Readonly<ButtonProps>) {
|
||||
</DialogTrigger>
|
||||
<DialogContent className='flex h-full flex-col overflow-hidden md:h-auto'>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('balanceRecharge')}</DialogTitle>
|
||||
<DialogTitle className={'text-4xl'}>{t('balanceRecharge')}</DialogTitle>
|
||||
<DialogDescription>{t('rechargeDescription')}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className='flex flex-col justify-between text-sm'>
|
||||
<div className='mt-8 flex flex-col justify-between text-sm'>
|
||||
<div className='grid gap-3'>
|
||||
<div className='font-semibold'>{t('rechargeAmount')}</div>
|
||||
<div className='flex'>
|
||||
<div className='mb-8 flex'>
|
||||
<EnhancedInput
|
||||
type='number'
|
||||
placeholder={t('enterAmount')}
|
||||
@@ -73,27 +73,30 @@ export default function Recharge(props: Readonly<ButtonProps>) {
|
||||
onChange={(value) => setParams({ ...params, payment: value })}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
className='fixed bottom-0 left-0 w-full rounded-none md:relative md:mt-6'
|
||||
disabled={loading || !params.amount}
|
||||
onClick={() => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
const response = await recharge(params);
|
||||
const orderNo = response.data.data?.order_no;
|
||||
if (orderNo) {
|
||||
router.push(`/payment?order_no=${orderNo}`);
|
||||
setOpen(false);
|
||||
<div className={'flex items-center justify-center'}>
|
||||
<AiroButton
|
||||
variant={'primary'}
|
||||
className='fixed bottom-0 left-0 md:relative md:mt-6'
|
||||
disabled={loading || !params.amount}
|
||||
onClick={() => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
const response = await recharge(params);
|
||||
const orderNo = response.data.data?.order_no;
|
||||
if (orderNo) {
|
||||
router.push(`/payment?order_no=${orderNo}`);
|
||||
setOpen(false);
|
||||
}
|
||||
} catch (error) {
|
||||
/* empty */
|
||||
}
|
||||
} catch (error) {
|
||||
/* empty */
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
{loading && <LoaderCircle className='mr-2 animate-spin' />}
|
||||
{t('rechargeNow')}
|
||||
</Button>
|
||||
});
|
||||
}}
|
||||
>
|
||||
{loading && <LoaderCircle className='mr-2 animate-spin' />}
|
||||
{t('rechargeNow')}
|
||||
</AiroButton>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
Reference in New Issue
Block a user