Compare commits
10 Commits
8241e95508
...
330a8c128b
| Author | SHA1 | Date | |
|---|---|---|---|
| 330a8c128b | |||
| e020a53020 | |||
| 8aa9b5694a | |||
| 804af6b1fc | |||
| f3d2590a44 | |||
| 09084eef21 | |||
| 8c7d415ae7 | |||
| b6c82e9bc9 | |||
| 6c40fde269 | |||
| 5db1e8b15a |
@ -15,7 +15,7 @@ unset APP_NAME # 显式销毁变量
|
||||
1. 执行 `./scripts/publish-dev.sh` 构建产物在 out-dev文件夹
|
||||
2. 上传out/zip.gz到服务器
|
||||
3. tar -xzvf ppanel-user-web-dev.tar.gz 解压
|
||||
4. tar -xzvf ppanel-user-web-dev.tar.gz
|
||||
4. tar -xzvf ppanel-admin-web-dev.tar.gz
|
||||
|
||||
```shell
|
||||
# 第一次启动
|
||||
|
||||
@ -20,16 +20,23 @@ import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
|
||||
import { formatDate } from '@workspace/ui/utils';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import Link from 'next/link';
|
||||
import { useRef, useState } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { UserDetail } from './user-detail';
|
||||
import UserForm from './user-form';
|
||||
|
||||
// 新的子组件,现在管理它自己的备注状态
|
||||
const RemarkForm = ({ onSave, initialRemark, CloseComponent }) => {
|
||||
const [remark, setRemark] = useState(initialRemark);
|
||||
// 为 RemarkForm 组件定义 props 类型
|
||||
interface RemarkFormProps {
|
||||
initialRemark?: string | null;
|
||||
onSave: (remark: string) => void;
|
||||
CloseComponent: React.ComponentType<{ asChild?: boolean; children: React.ReactNode }>;
|
||||
}
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
// 新的子组件,在管理它自己的备注状态
|
||||
const RemarkForm: React.FC<RemarkFormProps> = ({ onSave, initialRemark, CloseComponent }) => {
|
||||
const [remark, setRemark] = useState<string>(initialRemark ?? '');
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setRemark(event.target.value);
|
||||
};
|
||||
|
||||
|
||||
@ -59,11 +59,6 @@ export default function Page() {
|
||||
accessorKey: 'title',
|
||||
header: t('title'),
|
||||
},
|
||||
{
|
||||
accessorKey: 'user_id',
|
||||
header: t('user'),
|
||||
cell: ({ row }) => <UserDetail id={row.original.user_id} />,
|
||||
},
|
||||
{
|
||||
accessorKey: 'status',
|
||||
header: t('status.0'),
|
||||
@ -79,17 +74,22 @@ export default function Page() {
|
||||
},
|
||||
)}
|
||||
>
|
||||
{t(`status.${row.original.status}`)}
|
||||
{row.original.status === 4 ? t(`status.${row.original.status}`) : t(`status.1`)}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'user_id',
|
||||
header: t('user'),
|
||||
cell: ({ row }) => <UserDetail id={row.original.user_id} />,
|
||||
},
|
||||
{
|
||||
accessorKey: 'updated_at',
|
||||
header: t('updatedAt'),
|
||||
cell: ({ row }) => formatDate(row.getValue('updated_at')),
|
||||
},
|
||||
]}
|
||||
params={[
|
||||
/*params={[
|
||||
{
|
||||
key: 'status',
|
||||
placeholder: t('status.0'),
|
||||
@ -100,11 +100,12 @@ export default function Page() {
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
]}*/
|
||||
request={async (pagination, filters) => {
|
||||
const { data } = await getTicketList({
|
||||
...pagination,
|
||||
...filters,
|
||||
issue_type: 1,
|
||||
});
|
||||
return {
|
||||
list: data.data?.list || [],
|
||||
@ -166,11 +167,11 @@ export default function Page() {
|
||||
<div className={cn('bg-accent w-fit rounded-lg p-2 font-medium', {})}>
|
||||
<div> 提现金额:{ticket?.title?.split('-')[1]}</div>
|
||||
<div> 提现方式:{ticket?.description?.split('-')[0]}</div>
|
||||
{ticket?.description?.split('-')[1].includes('data:image') ? (
|
||||
{ticket?.description?.split('-')[1]?.includes('data:image') ? (
|
||||
<div>
|
||||
<div>收款码:</div>
|
||||
<NextImage
|
||||
src={ticket?.description?.split('-')[1]}
|
||||
src={ticket?.description?.split('-')[1]!}
|
||||
width={300}
|
||||
height={300}
|
||||
className='!size-auto object-cover'
|
||||
|
||||
@ -10,9 +10,11 @@ import { getLocale, getMessages } from 'next-intl/server';
|
||||
import { PublicEnvScript } from 'next-runtime-env';
|
||||
import { unstable_noStore as noStore } from 'next/cache';
|
||||
// import { Geist, Geist_Mono } from 'next/font/google';
|
||||
import { Inter } from 'next/font/google';
|
||||
import { cookies } from 'next/headers';
|
||||
import NextTopLoader from 'nextjs-toploader';
|
||||
import React from 'react';
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
|
||||
// const fontSans = Geist({
|
||||
// subsets: ['latin'],
|
||||
@ -112,7 +114,7 @@ export default async function RootLayout({
|
||||
<body
|
||||
suppressHydrationWarning
|
||||
// ${fontSans.variable} ${fontMono.variable}
|
||||
className={`size-full min-h-[calc(100dvh-env(safe-area-inset-top))] font-sans antialiased`}
|
||||
className={`size-full min-h-[calc(100dvh-env(safe-area-inset-top))] ${inter.className} antialiased`}
|
||||
>
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<NextTopLoader showSpinner={false} />
|
||||
|
||||
@ -107,7 +107,7 @@ export const navs = [
|
||||
{
|
||||
title: 'Withdraw Ticket Management',
|
||||
url: '/dashboard/withdraw-ticket',
|
||||
icon: 'flat-color-icons:collaboration',
|
||||
icon: 'flat-color-icons:debt',
|
||||
},
|
||||
{
|
||||
title: 'Document Management',
|
||||
|
||||
1
apps/admin/services/admin/typings.d.ts
vendored
1
apps/admin/services/admin/typings.d.ts
vendored
@ -864,6 +864,7 @@ declare namespace API {
|
||||
user_id?: number;
|
||||
status?: number;
|
||||
search?: string;
|
||||
issue_type?: number;
|
||||
};
|
||||
|
||||
type GetTicketListRequest = {
|
||||
|
||||
@ -28,9 +28,11 @@ import {
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@workspace/airo-ui/components/popover';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@workspace/airo-ui/components/tabs';
|
||||
import { differenceInDays } from '@workspace/airo-ui/utils';
|
||||
import Link from 'next/link';
|
||||
import { QRCodeCanvas } from 'qrcode.react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
|
||||
interface SubscribeCardProps {
|
||||
userSubscribeData: API.UserSubscribe;
|
||||
protocol: string[];
|
||||
@ -53,7 +55,7 @@ const SubscribeCard = (props: SubscribeCardProps) => {
|
||||
if (list.length > 0) {
|
||||
setUserSubscribeProtocolCurrent(0);
|
||||
}
|
||||
}, [props.userSubscribeData.token, protocol]);
|
||||
}, [props.userSubscribeData?.token, protocol]);
|
||||
|
||||
return (
|
||||
<div className='space-y-2 sm:space-y-4'>
|
||||
@ -69,11 +71,15 @@ const SubscribeCard = (props: SubscribeCardProps) => {
|
||||
{t('totalTraffic')}
|
||||
</p>
|
||||
<p className='text-xs font-medium text-[#0F2C53] sm:text-base'>
|
||||
<Display
|
||||
type='traffic'
|
||||
value={userSubscribeData.traffic}
|
||||
unlimited={!userSubscribeData.traffic}
|
||||
/>
|
||||
{userSubscribeData?.status === 1 ? (
|
||||
<Display
|
||||
type='traffic'
|
||||
value={userSubscribeData.traffic}
|
||||
unlimited={!userSubscribeData.traffic}
|
||||
/>
|
||||
) : (
|
||||
'0.00GB'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
@ -81,9 +87,15 @@ const SubscribeCard = (props: SubscribeCardProps) => {
|
||||
{t('nextResetDays')}
|
||||
</p>
|
||||
<p className='text-xs font-medium text-[#0F2C53] sm:text-base'>
|
||||
{userSubscribeData.reset_time
|
||||
? differenceInDays(new Date(userSubscribeData.reset_time), new Date())
|
||||
: t('noReset')}
|
||||
{userSubscribeData?.status === 1 ? (
|
||||
<>
|
||||
{userSubscribeData.reset_time
|
||||
? differenceInDays(new Date(userSubscribeData.reset_time), new Date())
|
||||
: t('noReset')}
|
||||
</>
|
||||
) : (
|
||||
'N/A'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
@ -91,17 +103,30 @@ const SubscribeCard = (props: SubscribeCardProps) => {
|
||||
{t('expirationDays')}
|
||||
</p>
|
||||
<p className='text-xs font-medium text-[#0F2C53] sm:text-base'>
|
||||
{userSubscribeData.expire_time
|
||||
? differenceInDays(new Date(userSubscribeData.expire_time), new Date()) ||
|
||||
t('unknown')
|
||||
: t('noLimit')}
|
||||
{userSubscribeData?.status === 1 ? (
|
||||
<>
|
||||
{userSubscribeData.expire_time
|
||||
? differenceInDays(new Date(userSubscribeData.expire_time), new Date()) ||
|
||||
t('unknown')
|
||||
: t('noLimit')}
|
||||
</>
|
||||
) : (
|
||||
'N/A'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 订阅链接 */}
|
||||
<div className='rounded-[26px] bg-[#EAEAEA] p-2 sm:p-4'>
|
||||
<div className='relative rounded-[26px] bg-[#EAEAEA] p-2 sm:p-4'>
|
||||
{userSubscribeData?.status !== 1 && (
|
||||
<div className='absolute inset-0 z-40 flex h-full w-full items-center justify-center rounded-[26px] border-4 border-[#D9D9D9] bg-white/50 backdrop-blur-[1px]'>
|
||||
<AiroButton variant={'primary'} asChild>
|
||||
<Link href={'/subscribe'}>{t('buySubscriptionNow')}</Link>
|
||||
</AiroButton>
|
||||
</div>
|
||||
)}
|
||||
<div className='mb-3 flex flex-wrap justify-between gap-4'>
|
||||
{props.protocol.length > 1 && (
|
||||
<Tabs
|
||||
@ -126,21 +151,21 @@ const SubscribeCard = (props: SubscribeCardProps) => {
|
||||
<div className={'mb-3 flex items-center justify-center gap-3'}>
|
||||
<div
|
||||
className={
|
||||
'flex flex-1 items-center gap-1 rounded-full bg-[#BABABA] pl-1 sm:gap-2 sm:rounded-[16px] sm:pl-2'
|
||||
'flex flex-1 items-center gap-1 rounded-full bg-[#BABABA] pl-1 sm:gap-2 sm:pl-2'
|
||||
}
|
||||
>
|
||||
<Select
|
||||
value={userSubscribeProtocolCurrent}
|
||||
onValueChange={setUserSubscribeProtocolCurrent}
|
||||
>
|
||||
<SelectTrigger className='h-auto w-auto flex-shrink-0 rounded-[16px] border-none bg-[#D9D9D9] px-2.5 py-0.5 text-[13px] font-medium text-[#0F2C53] shadow-none hover:bg-[#848484] focus:ring-0 sm:h-[35px] sm:rounded-[8px] sm:px-2 sm:py-1.5 [&>svg]:hidden'>
|
||||
<SelectTrigger className='h-auto w-auto flex-shrink-0 rounded-[16px] border-none bg-[#D9D9D9] px-2.5 py-0.5 text-[13px] font-medium text-[#0F2C53] shadow-none hover:bg-[#848484] focus:ring-0 sm:h-[35px] sm:px-2 sm:py-1.5 [&>svg]:hidden'>
|
||||
<SelectValue>
|
||||
<div className='flex flex-col items-center justify-between text-[10px] sm:text-xs'>
|
||||
<div>
|
||||
{t('subscriptionUrl')}
|
||||
{userSubscribeProtocolCurrent + 1}
|
||||
</div>
|
||||
<div className='-mt-0.5 h-0 w-0 scale-50 border-l-[3px] border-r-[3px] border-t-[3px] border-l-transparent border-r-transparent border-t-[#0F2C53] sm:scale-100'></div>
|
||||
<div className='h-0 w-0 scale-50 border-l-[3px] border-r-[3px] border-t-[3px] border-l-transparent border-r-transparent border-t-[#0F2C53] sm:scale-100'></div>
|
||||
</div>
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
@ -158,7 +183,7 @@ const SubscribeCard = (props: SubscribeCardProps) => {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<div className='flex-1 rounded-full bg-white px-3 py-2 text-[10px] leading-tight text-[#225BA9] shadow-[inset_0px_0px_7.6px_0px_rgba(0,0,0,0.25)] sm:rounded-[16px]'>
|
||||
<div className='flex-1 rounded-full bg-white px-3 py-2 text-[10px] leading-tight text-[#225BA9] shadow-[inset_0px_0px_7.6px_0px_rgba(0,0,0,0.25)]'>
|
||||
<div className={'flex items-center gap-4 py-1'}>
|
||||
<div className={'line-clamp-2 flex-1 break-all'}>
|
||||
{userSubscribeProtocol[userSubscribeProtocolCurrent]}
|
||||
@ -203,7 +228,7 @@ const SubscribeCard = (props: SubscribeCardProps) => {
|
||||
<div className='flex justify-between gap-2'>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<AiroButton variant='danger' className={'px-2 text-xs'}>
|
||||
<AiroButton variant='primaryBlue' className={'px-2 text-xs'}>
|
||||
{t('resetSubscription')}
|
||||
</AiroButton>
|
||||
</AlertDialogTrigger>
|
||||
@ -231,7 +256,7 @@ const SubscribeCard = (props: SubscribeCardProps) => {
|
||||
<Renewal
|
||||
className='px-2 text-xs'
|
||||
id={userSubscribeData.id}
|
||||
subscribe={userSubscribeData.subscribe}
|
||||
subscribe={userSubscribeData.subscribe || {}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -7,6 +7,7 @@ import useGlobalStore from '@/config/use-global';
|
||||
import { getStat } from '@/services/common/common';
|
||||
import { queryUserSubscribe } from '@/services/user/user';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { AiroButton } from '@workspace/airo-ui/components/AiroButton';
|
||||
import { Button } from '@workspace/airo-ui/components/button';
|
||||
import { Card } from '@workspace/airo-ui/components/card';
|
||||
import { useTranslations } from 'next-intl';
|
||||
@ -23,9 +24,9 @@ import {
|
||||
PopupRef,
|
||||
} from '@/app/(main)/(content)/(user)/dashboard/components/Announcement/Popup';
|
||||
import { Empty } from '@/components/empty';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import { queryAnnouncement } from '@/services/user/announcement';
|
||||
import { queryOrderList } from '@/services/user/order';
|
||||
import { default as Airo_Empty } from '@workspace/airo-ui/custom-components/empty';
|
||||
import { formatDate } from '@workspace/airo-ui/utils';
|
||||
|
||||
const platforms: (keyof API.ApplicationPlatform)[] = [
|
||||
@ -40,11 +41,13 @@ const platforms: (keyof API.ApplicationPlatform)[] = [
|
||||
export default function Content() {
|
||||
const t = useTranslations('dashboard');
|
||||
|
||||
const { data: userSubscribe = [], refetch } = useQuery({
|
||||
const { data: userSubscribe = {}, refetch } = useQuery({
|
||||
queryKey: ['queryUserSubscribe'],
|
||||
queryFn: async () => {
|
||||
const { data } = await queryUserSubscribe();
|
||||
return data.data?.list || [];
|
||||
const activeList = data.data?.list?.filter((v) => v.status === 1);
|
||||
|
||||
return activeList[0] ?? {};
|
||||
},
|
||||
});
|
||||
|
||||
@ -91,11 +94,126 @@ export default function Content() {
|
||||
|
||||
const popupRef = useRef<PopupRef>(null);
|
||||
const dialogRef = useRef<DialogRef>(null);
|
||||
|
||||
function openPopupWindow(item) {
|
||||
// features 字符串用于控制窗口的特性
|
||||
const pageWidth = 600; // 弹出窗口的宽度
|
||||
const pageHeight = 550; // 弹出窗口的高度
|
||||
const {
|
||||
availLeft, // 返回浏览器可用空间左边距离屏幕(系统桌面)左边界的距离。
|
||||
availHeight, // 浏览器在显示屏上的可用高度,即当前屏幕高度
|
||||
availWidth, // 浏览器在显示屏上的可用宽度,即当前屏幕宽度
|
||||
} = window.screen;
|
||||
const pageTop = (availHeight - pageHeight) / 2; // 窗口的垂直位置
|
||||
let pageLeft = (availWidth - pageWidth) / 2; // 窗口的水平位置;
|
||||
|
||||
pageLeft += availLeft; // 加上屏幕偏移值
|
||||
|
||||
const features = `width=${pageWidth},height=${pageHeight},left=${pageLeft},top=${pageTop},toolbar=no,location=no,menubar=no`;
|
||||
console.log(features);
|
||||
window.open(item.download, item.title, features);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'grid grid-cols-1 gap-[10px] sm:gap-6 lg:grid-cols-2'}>
|
||||
<div className={'grid grid-cols-1 gap-[10px] sm:gap-5 2xl:grid-cols-4'}>
|
||||
{/* 快捷下载 Card */}
|
||||
<Card className='rounded-[20px] border border-[#D9D9D9] p-6 shadow-[0px_0px_52.6px_1px_rgba(15,44,83,0.05)] 2xl:order-2 2xl:col-span-1'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<h3 className='text-base font-medium text-[#666666] sm:text-xl'>
|
||||
{t('quickDownloads')}
|
||||
</h3>
|
||||
</div>
|
||||
<div className={'text-xs font-normal leading-[1.5] text-[#666]'}>{t('selectOS')}</div>
|
||||
|
||||
<div
|
||||
className={
|
||||
'mt-2.5 flex items-center justify-around rounded-2xl bg-[#EAEAEA] px-4 pb-3 pt-[18px] text-xs text-[#0F2C53]'
|
||||
}
|
||||
>
|
||||
{[
|
||||
{
|
||||
label: 'iOS',
|
||||
icon: 'Group 77',
|
||||
href: 'https://apps.apple.com/us/app/shadowrocket/id932747118?l=zh-Hans-CN',
|
||||
},
|
||||
{
|
||||
label: 'Mac',
|
||||
icon: 'Group 77',
|
||||
href: 'https://apps.apple.com/us/app/shadowrocket/id932747118?l=zh-Hans-CN',
|
||||
},
|
||||
{
|
||||
label: 'Win',
|
||||
icon: 'Group 75',
|
||||
href: 'https://down.airoport.xin/Hiddify-Windows-Setup-x64.msix',
|
||||
},
|
||||
{
|
||||
label: 'Android',
|
||||
icon: 'Group 75',
|
||||
href: 'https://down.airoport.xin/Hiddify-Android-universal.apk',
|
||||
},
|
||||
].map((v) => {
|
||||
return (
|
||||
<a
|
||||
key={v.label}
|
||||
href={v.href}
|
||||
target={'_blank'}
|
||||
className={'cursor-pointer text-center'}
|
||||
>
|
||||
<div className={''}>
|
||||
<SvgIcon name={v.icon}></SvgIcon>
|
||||
</div>
|
||||
{v.label}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
'mt-2.5 flex h-[37px] items-center justify-between rounded-full bg-[#EAEAEA] pl-4 text-[#666]'
|
||||
}
|
||||
>
|
||||
<span className={'text-sm font-medium'}>{t('freeAppleID')}</span>
|
||||
<AiroButton
|
||||
className={'m-1'}
|
||||
variant={'primary'}
|
||||
onClick={() => {
|
||||
openPopupWindow({
|
||||
download: 'https://aunlock.laomaos.com/share/DEXVzMSP',
|
||||
title: 'Shadowrocket',
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('get')}
|
||||
</AiroButton>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* 我的订阅 Card */}
|
||||
<Card className='rounded-[20px] border border-[#D9D9D9] p-6 shadow-[0px_0px_52.6px_1px_rgba(15,44,83,0.05)] 2xl:order-3 2xl:col-span-2'>
|
||||
<div className=''>
|
||||
<div className={'flex items-center justify-between'}>
|
||||
<h3 className='text-base font-medium text-[#666666] sm:text-xl'>
|
||||
{t('mySubscription')}
|
||||
</h3>
|
||||
<Link
|
||||
href={'/document'}
|
||||
className='border-0 bg-transparent p-0 text-sm font-semibold text-[#225BA9] shadow-none outline-0 hover:bg-transparent sm:font-normal'
|
||||
>
|
||||
{t('beginnerTutorial')}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SubScribeCard
|
||||
userSubscribeData={userSubscribe}
|
||||
protocol={data?.protocol || []}
|
||||
refetch={refetch}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* 账户概况 Card */}
|
||||
<Card className='rounded-[20px] border border-[#D9D9D9] p-6 shadow-[0px_0px_52.6px_1px_rgba(15,44,83,0.05)]'>
|
||||
<Card className='rounded-[20px] border border-[#D9D9D9] p-6 shadow-[0px_0px_52.6px_1px_rgba(15,44,83,0.05)] 2xl:order-1 2xl:col-span-1'>
|
||||
<div className='mb-1'>
|
||||
<h3 className='text-base font-medium text-[#666666] sm:text-xl'>
|
||||
{t('accountOverview')}
|
||||
@ -107,7 +225,7 @@ export default function Content() {
|
||||
|
||||
<div className='mb-3 sm:mb-3.5'>
|
||||
<span className='text-2xl font-medium text-[#091B33]'>
|
||||
{userSubscribe?.length > 0 && userSubscribe[0]?.status === 1 && orderData
|
||||
{userSubscribe?.status === 1 && orderData
|
||||
? orderData?.quantity === 1
|
||||
? t('annualMonthPlanUser')
|
||||
: t('annualYearPlanUser')
|
||||
@ -120,7 +238,7 @@ export default function Content() {
|
||||
<span className='text-sm font-light text-[#666666]'>{t('accountBalance')}</span>
|
||||
<Recharge
|
||||
className={
|
||||
'min-w-[50px] border-0 bg-transparent p-0 text-sm font-normal text-[#225BA9] shadow-none outline-0 hover:bg-transparent'
|
||||
'min-w-[50px] border-0 bg-transparent p-0 text-sm font-semibold text-[#225BA9] shadow-none outline-0 hover:bg-transparent'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
@ -131,101 +249,118 @@ export default function Content() {
|
||||
</Card>
|
||||
|
||||
{/* 套餐状态 Card */}
|
||||
<Card className='rounded-[20px] border border-[#D9D9D9] p-6 text-[#666666] shadow-[0px_0px_52.6px_1px_rgba(15,44,83,0.05)]'>
|
||||
<Card className='2xl:order-0 rounded-[20px] border border-[#D9D9D9] p-6 text-[#666666] shadow-[0px_0px_52.6px_1px_rgba(15,44,83,0.05)] 2xl:col-span-2'>
|
||||
<div className=''>
|
||||
<h3 className='flex items-center justify-between text-[#666666]'>
|
||||
<div className={'flex items-center justify-between'}>
|
||||
<span className={'text-base font-medium sm:text-xl'}>{t('planStatus')}</span>
|
||||
{userSubscribe?.length > 0 && userSubscribe?.[0]?.status === 1 ? (
|
||||
{userSubscribe?.status === 1 ? (
|
||||
<span className={'ml-2.5 rounded-full bg-[#A8D4ED] px-2 text-[8px] text-white'}>
|
||||
{t('inEffect')}
|
||||
</span>
|
||||
) : null}
|
||||
) : (
|
||||
<span className={'ml-2.5 rounded-full bg-[#666666] px-2 text-[8px] text-white'}>
|
||||
{t('notEffect')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<ResetTraffic
|
||||
className={
|
||||
'border-0 bg-transparent p-0 text-sm font-normal text-[#225BA9] shadow-none outline-0 hover:bg-transparent'
|
||||
}
|
||||
id={userSubscribe?.[0]?.id || 0}
|
||||
replacement={userSubscribe?.[0]?.subscribe.replacement}
|
||||
id={userSubscribe?.id || 0}
|
||||
replacement={userSubscribe?.subscribe?.replacement}
|
||||
/>
|
||||
</h3>
|
||||
</div>
|
||||
{userSubscribe?.length ? (
|
||||
<>
|
||||
<div className='mt-1 text-xs text-[#666666] sm:text-sm'>
|
||||
{t('planExpirationTime')}
|
||||
{formatDate(userSubscribe?.[0]?.expire_time, false)}
|
||||
</div>
|
||||
<div className='mb-3 sm:mb-5'>
|
||||
<span className='text-2xl font-medium text-[#091B33]'>
|
||||
{userSubscribe?.[0]?.subscribe.name}
|
||||
</span>
|
||||
</div>
|
||||
<div className='mb-4 flex items-center justify-between'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='text-xs sm:text-sm'>{t('availableDevices')}</span>
|
||||
<div className='flex gap-2'>
|
||||
{Array.from({ length: userSubscribe?.[0]?.subscribe.device_limit }).map(
|
||||
(_, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`h-4 w-4 rounded-full ${index < (userSubscribe?.[0]?.subscribe.device_limit || 0) > 1 ? 'bg-[#225BA9]' : 'bg-[#D9D9D9]'}`}
|
||||
></div>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className='mt-1 text-xs font-light text-[#666666] sm:text-sm'>
|
||||
{t('planExpirationTime')}
|
||||
{formatDate(userSubscribe?.expire_time, false) || t('None')}
|
||||
</div>
|
||||
<div className='mb-3 mt-1 sm:mb-5'>
|
||||
<span className='text-2xl font-medium text-[#091B33]'>
|
||||
{userSubscribe?.subscribe?.name ? (
|
||||
userSubscribe?.subscribe?.name
|
||||
) : (
|
||||
<span className={'text-[#848484]'}>{t('noPlanAvailable')}</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className='mb-4 flex items-center justify-between'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='text-xs sm:text-sm'>{t('availableDevices')}</span>
|
||||
<div className='flex gap-2'>
|
||||
{Array.from({ length: userSubscribe?.subscribe?.device_limit || 6 }).map(
|
||||
(_, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`h-2.5 w-2.5 rounded-full sm:h-4 sm:w-4 ${index < (userSubscribe?.subscribe?.device_limit || 0) > 1 ? 'bg-[#225BA9]' : 'bg-[#D9D9D9]'}`}
|
||||
></div>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<span className='text-xs font-light sm:text-sm'>
|
||||
{t('online')}
|
||||
{data?.online_device || 0}/{userSubscribe?.subscribe?.device_limit || 0}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className='mb-1 flex items-center justify-between font-light'>
|
||||
<span className='text-xs sm:text-sm'>
|
||||
{t('online')}
|
||||
{data?.online_device} / {userSubscribe?.[0]?.subscribe.device_limit}
|
||||
{t('usedTrafficTotalTraffic')}
|
||||
{userSubscribe?.subscribe?.device_limit ? (
|
||||
<>
|
||||
<Display
|
||||
type='traffic'
|
||||
value={userSubscribe?.upload + userSubscribe?.download}
|
||||
unlimited={!userSubscribe?.traffic}
|
||||
/>
|
||||
/
|
||||
<Display
|
||||
type='traffic'
|
||||
value={userSubscribe?.traffic || 0}
|
||||
unlimited={!userSubscribe?.traffic}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
'0GB/0GB'
|
||||
)}
|
||||
</span>
|
||||
<span className='text-xs sm:text-sm'>
|
||||
{t('remaining')}
|
||||
{userSubscribe?.status === 1 ? (
|
||||
<>
|
||||
{100 -
|
||||
Math.round(
|
||||
(((userSubscribe?.upload || 0) + (userSubscribe?.download || 0)) /
|
||||
(userSubscribe?.traffic || 1)) *
|
||||
100,
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
0
|
||||
)}
|
||||
%
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className='mb-1 flex items-center justify-between'>
|
||||
<span className='text-xs sm:text-sm'>
|
||||
{t('usedTrafficTotalTraffic')}
|
||||
<Display
|
||||
type='traffic'
|
||||
value={userSubscribe?.[0]?.upload + userSubscribe?.[0]?.download}
|
||||
unlimited={!userSubscribe?.[0]?.traffic}
|
||||
/>
|
||||
/{' '}
|
||||
<Display
|
||||
type='traffic'
|
||||
value={userSubscribe?.[0]?.traffic}
|
||||
unlimited={!userSubscribe?.[0]?.traffic}
|
||||
/>
|
||||
</span>
|
||||
<span className='text-xs sm:text-sm'>
|
||||
{t('remaining')}
|
||||
{100 -
|
||||
Math.round(
|
||||
(((userSubscribe?.[0]?.upload || 0) + (userSubscribe?.[0]?.download || 0)) /
|
||||
(userSubscribe?.[0]?.traffic || 1)) *
|
||||
100,
|
||||
)}
|
||||
%
|
||||
</span>
|
||||
</div>
|
||||
<div className='flex h-5 w-full items-center rounded-[20px] bg-[#EAEAEA] p-0.5'>
|
||||
<div
|
||||
className={'h-full rounded-[20px] bg-[#225BA9]'}
|
||||
style={{
|
||||
width: `${Math.round((((userSubscribe?.[0]?.upload || 0) + (userSubscribe?.[0]?.download || 0)) / (userSubscribe?.[0]?.traffic || 1)) * 100)}%`,
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div className='flex h-5 w-full items-center rounded-[20px] bg-[#EAEAEA] p-0.5'>
|
||||
<div
|
||||
className={'h-full rounded-[20px] bg-[#225BA9]'}
|
||||
style={{
|
||||
width: `${Math.round((((userSubscribe?.upload || 0) + (userSubscribe?.download || 0)) / (userSubscribe?.traffic || 1)) * 100)}%`,
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<Airo_Empty className={'py-0'} description={t('noPlanAvailable')} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* 网站公告 Card */}
|
||||
<Card className='relative order-4 rounded-[20px] border border-[#EAEAEA] bg-gradient-to-b from-white to-[#EAEAEA] p-6 pb-0 sm:order-none'>
|
||||
<Card className='relative rounded-[20px] border border-[#EAEAEA] bg-gradient-to-b from-white to-[#EAEAEA] p-6 pb-0 2xl:order-4 2xl:col-span-2'>
|
||||
<div className='mb-3 flex items-center justify-between sm:mb-1'>
|
||||
<h3 className='text-base font-medium text-[#666666] sm:text-xl'>
|
||||
{t('siteAnnouncements')}
|
||||
@ -282,31 +417,6 @@ export default function Content() {
|
||||
<Popup ref={popupRef} />
|
||||
<AnnouncementDialog ref={dialogRef} />
|
||||
</Card>
|
||||
|
||||
{/* 我的订阅 Card */}
|
||||
<Card className='rounded-[20px] border border-[#D9D9D9] p-6 shadow-[0px_0px_52.6px_1px_rgba(15,44,83,0.05)]'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<h3 className='text-base font-medium text-[#666666] sm:text-xl'>
|
||||
{t('mySubscription')}
|
||||
</h3>
|
||||
<Link
|
||||
href={'/document'}
|
||||
className='border-0 bg-transparent p-0 text-sm font-semibold text-[#225BA9] shadow-none outline-0 hover:bg-transparent sm:font-normal'
|
||||
>
|
||||
{t('beginnerTutorial')}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{userSubscribe?.[0] && data?.protocol ? (
|
||||
<SubScribeCard
|
||||
userSubscribeData={userSubscribe?.[0]}
|
||||
protocol={data.protocol}
|
||||
refetch={refetch}
|
||||
/>
|
||||
) : (
|
||||
<Empty />
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -168,7 +168,7 @@ export function TutorialButton({ items }: { items: Item[] }) {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={'flex'}>
|
||||
{item.download ? (
|
||||
<button
|
||||
className={cn(
|
||||
|
||||
@ -85,7 +85,7 @@ export default function Page() {
|
||||
<Card className='border-none shadow-none sm:border sm:shadow'>
|
||||
<CardContent className='grid gap-2 p-6 text-sm'>
|
||||
<div className='relative'>
|
||||
<div className={'absolute flex gap-3'}>
|
||||
<div className={'flex gap-3 sm:absolute'}>
|
||||
<div
|
||||
className={
|
||||
'flex items-center justify-between rounded-md border-2 border-[#225BA9] p-0.5'
|
||||
|
||||
@ -55,7 +55,7 @@ const WalletDialog: WalletDialogProps = (props) => {
|
||||
const { currency } = common;
|
||||
|
||||
// 定义支持的账户类型
|
||||
const ACCOUNT_TYPE = ['USDT', '微信', '支付宝'] as const;
|
||||
const ACCOUNT_TYPE = ['USDT(TRC20)', '微信', '支付宝'] as const;
|
||||
|
||||
// 根据账户类型定义 Zod 验证模式
|
||||
const formSchema = z
|
||||
@ -76,11 +76,11 @@ const WalletDialog: WalletDialogProps = (props) => {
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
// 根据提现方式进行条件验证
|
||||
if (data.type === 'USDT') {
|
||||
if (data.type === 'USDT(TRC20)') {
|
||||
if (!data.account || data.account.trim().length === 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'USDT 账号不能为空',
|
||||
message: 'USDT(TRC20) 账号不能为空',
|
||||
path: ['account'],
|
||||
});
|
||||
}
|
||||
@ -111,7 +111,7 @@ const WalletDialog: WalletDialogProps = (props) => {
|
||||
// 构建 title 和 description
|
||||
const title = `提现金额-${data.money}`;
|
||||
let description = '';
|
||||
if (data.type === 'USDT') {
|
||||
if (data.type === 'USDT(TRC20)') {
|
||||
description = `${data.type}-${data.account || ''}`;
|
||||
} else if (data.type === '微信' || data.type === '支付宝') {
|
||||
description = `${data.type}-${data.avatar || ''}`;
|
||||
@ -151,7 +151,7 @@ const WalletDialog: WalletDialogProps = (props) => {
|
||||
size: 1,
|
||||
issue_type: 1,
|
||||
});
|
||||
if (ticketData?.list?.length > 1) {
|
||||
if (ticketData?.list?.length > 0) {
|
||||
toast.info('已经存在待处理提现,请耐心等待');
|
||||
return;
|
||||
}
|
||||
@ -181,7 +181,7 @@ const WalletDialog: WalletDialogProps = (props) => {
|
||||
|
||||
// 根据提现方式动态生成弹窗描述
|
||||
const getModalDescription = () => {
|
||||
if (currentType === 'USDT') {
|
||||
if (currentType === 'USDT(TRC20)') {
|
||||
const accountInfo = account || '未知地址';
|
||||
return `请确认您的提现地址及金额无误,您将提现${money}RMB至${accountInfo}地址账号。`;
|
||||
} else {
|
||||
@ -214,7 +214,7 @@ const WalletDialog: WalletDialogProps = (props) => {
|
||||
</DialogHeader>
|
||||
<div className={'pt-4'}>
|
||||
<div className={'pb-2 text-sm font-semibold text-[#7A7A7A]'}>
|
||||
{currentType === 'USDT'
|
||||
{currentType === 'USDT(TRC20)'
|
||||
? '将佣金提现至您的个人数字钱包,无手续费'
|
||||
: '该提现方式需10%手续费,该费率由支付平台收取'}
|
||||
</div>
|
||||
@ -250,8 +250,8 @@ const WalletDialog: WalletDialogProps = (props) => {
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* 提现账号输入(仅当选择USDT时显示) */}
|
||||
{currentType === 'USDT' && (
|
||||
{/* 提现账号输入(仅当选择USDT(TRC20)时显示) */}
|
||||
{currentType === 'USDT(TRC20)' && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='account'
|
||||
|
||||
25
apps/user/assets/svg-icon/Group 75.svg
Normal file
25
apps/user/assets/svg-icon/Group 75.svg
Normal file
@ -0,0 +1,25 @@
|
||||
<svg width="58" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d_2526_1784)">
|
||||
<rect x="2.11401" y="2.06323" width="53.6962" height="53.6962" rx="10" fill="white"/>
|
||||
<g clip-path="url(#clip0_2526_1784)">
|
||||
<path d="M35.2874 19.2087C35.2874 18.9251 35.4299 18.6623 35.663 18.5164L41.8535 14.64C42.3693 14.3171 43.0255 14.7047 43.0255 15.3324V21.9808C43.0255 22.4267 42.679 22.7882 42.2517 22.7882H36.0612C35.6339 22.7882 35.2874 22.4267 35.2874 21.9808V19.2087Z" fill="#455FE9"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.8295 24.9768C24.5964 25.1227 24.4538 25.3855 24.4538 25.6691V33.2867V34.0936V34.0941V35.3041C24.4538 35.75 24.1074 36.0712 23.68 36.0712H22.1323C21.705 36.0712 21.3585 35.75 21.3585 35.3041V34.0936V29.06C21.3585 28.4323 20.7023 28.0447 20.1866 28.3676L13.996 32.2439C13.763 32.3899 13.6204 32.6526 13.6204 32.9364V41.3613C13.6204 41.8072 13.9668 42.1687 14.3942 42.1687H20.5847C21.0121 42.1687 21.3585 41.8072 21.3585 41.3613V40.5533V39.5381C21.3585 39.0921 21.705 38.8219 22.1323 38.8219H23.68C24.1074 38.8219 24.4538 39.0921 24.4538 39.5381V40.5533V41.3613C24.4538 41.8072 24.8003 42.1687 25.2276 42.1687H31.4181C31.8455 42.1687 32.192 41.8072 32.192 41.3613V34.0941V33.2867V21.7929C32.192 21.1652 31.5358 20.7775 31.0199 21.1005L24.8295 24.9768Z" fill="#455FE9"/>
|
||||
<path d="M35.2878 26.0989V41.4406C35.2878 41.8643 35.6343 42.2077 36.0617 42.2077H42.2522C42.6795 42.2077 43.026 41.8643 43.026 41.4406V26.0989C43.026 25.6752 42.6795 25.3318 42.2522 25.3318H36.0617C35.6343 25.3318 35.2878 25.6752 35.2878 26.0989Z" fill="#455FE9"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_2526_1784" x="0.114014" y="0.0632324" width="57.6963" height="57.6963" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="1"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_2526_1784"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_2526_1784" result="shape"/>
|
||||
</filter>
|
||||
<clipPath id="clip0_2526_1784">
|
||||
<rect width="29.4051" height="30.6835" fill="white" transform="translate(13.6204 12.2913)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
22
apps/user/assets/svg-icon/Group 77.svg
Normal file
22
apps/user/assets/svg-icon/Group 77.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 8.0 KiB |
@ -1,6 +1,6 @@
|
||||
// 加载状态组件
|
||||
import { unitConversion } from '@workspace/airo-ui/utils';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useRef } from 'react';
|
||||
|
||||
const LoadingState = () => {
|
||||
const t = useTranslations('components.offerDialog');
|
||||
@ -338,16 +338,17 @@ const TabContent: React.FC<TabContentProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
// 使用 useMemo 优化数据处理性能
|
||||
const yearlyPlans: PlanProps[] = useMemo(
|
||||
() => subscribeData?.map((item) => processPlanData(item, true)),
|
||||
[subscribeData],
|
||||
);
|
||||
console.log('subscribeData:', subscribeData);
|
||||
|
||||
const monthlyPlans: PlanProps[] = useMemo(
|
||||
() => subscribeData?.map((item) => processPlanData(item, false)),
|
||||
[subscribeData],
|
||||
);
|
||||
let yearlyPlans: PlanProps[] = [];
|
||||
if (Array.isArray(subscribeData)) {
|
||||
yearlyPlans = subscribeData.map((item) => processPlanData(item, true));
|
||||
}
|
||||
|
||||
let monthlyPlans: PlanProps[] = [];
|
||||
if (Array.isArray(subscribeData)) {
|
||||
monthlyPlans = subscribeData.map((item) => processPlanData(item, false));
|
||||
}
|
||||
|
||||
const PurchaseRef = useRef<{
|
||||
show: (subscribe: API.Subscribe, tabValue: string) => void;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"None": "None",
|
||||
"accountBalance": "Account Balance",
|
||||
"accountOverview": "Account Overview",
|
||||
"address1": "Address 1",
|
||||
@ -7,11 +8,13 @@
|
||||
"annualYearPlanUser": "Annual Plan User",
|
||||
"availableDevices": "Available Devices",
|
||||
"beginnerTutorial": "Beginner Tutorial",
|
||||
"buySubscriptionNow": "Buy Subscription Now",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Confirm",
|
||||
"confirmResetSubscription": "Are you sure you want to reset the subscription address?",
|
||||
"copy": "Copy",
|
||||
"copyFailure": "Copy failed, please copy manually",
|
||||
"copySubscribeLink": "Copy the subscription link or click the QR code button to scan",
|
||||
"copySubscriptionLinkOrScanQrCode": "Copy subscription link or click the QR code button to scan",
|
||||
"copySuccess": "Copy successful",
|
||||
"deducted": "Canceled",
|
||||
@ -19,6 +22,8 @@
|
||||
"expirationDays": "Expiration Days",
|
||||
"expired": "Expired",
|
||||
"finished": "Traffic exhausted",
|
||||
"freeAppleID": "Free US Apple ID",
|
||||
"get": "Get",
|
||||
"import": "Import",
|
||||
"inEffect": "In Effect",
|
||||
"latestAnnouncement": "Latest Announcement",
|
||||
@ -32,6 +37,7 @@
|
||||
"noPlanAvailable": "No Plan Available",
|
||||
"noReset": "No Reset",
|
||||
"noYPlan": "No Plan Active",
|
||||
"notEffect": "Not in effect",
|
||||
"online": "Online: ",
|
||||
"pageOf": "Page {pageIndex} of {pageCount}",
|
||||
"pinnedAnnouncement": "[Pinned]",
|
||||
@ -40,12 +46,14 @@
|
||||
"prompt": "Prompt",
|
||||
"purchaseSubscription": "Purchase Subscription",
|
||||
"qrCode": "QR Code",
|
||||
"quickDownloads": "Quick Downloads",
|
||||
"remaining": "Remaining: ",
|
||||
"resetSubscription": "Reset Subscription Address",
|
||||
"resetSuccess": "Reset successful",
|
||||
"rowsPerPage": "Rows per page",
|
||||
"scanCodeToSubscribe": "Scan code to subscribe",
|
||||
"scanToSubscribe": "Scan to Subscribe",
|
||||
"selectOS": "Select the corresponding operating system to download the client",
|
||||
"siteAnnouncements": "Site Announcements",
|
||||
"subscriptionUrl": "Subscription URL",
|
||||
"totalTraffic": "Total Traffic",
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"None": "暂无",
|
||||
"accountBalance": "账户余额",
|
||||
"accountOverview": "账户概况",
|
||||
"address1": "地址1",
|
||||
@ -7,11 +8,13 @@
|
||||
"annualYearPlanUser": "年度套餐用户",
|
||||
"availableDevices": "可用设备",
|
||||
"beginnerTutorial": "新手教程",
|
||||
"buySubscriptionNow": "立即购买订阅",
|
||||
"cancel": "取消",
|
||||
"confirm": "确认",
|
||||
"confirmResetSubscription": "是否确认重置订阅地址?",
|
||||
"copy": "复制",
|
||||
"copyFailure": "复制失败,请手动复制",
|
||||
"copySubscribeLink": "复制订阅链接或点击二维码按钮扫码",
|
||||
"copySubscriptionLinkOrScanQrCode": "复制订阅链接或点击二维码按钮扫码",
|
||||
"copySuccess": "复制成功",
|
||||
"deducted": "已取消",
|
||||
@ -19,6 +22,8 @@
|
||||
"expirationDays": "到期时间/天",
|
||||
"expired": "已过期",
|
||||
"finished": "流量已用尽",
|
||||
"freeAppleID": "免费美区Apple ID",
|
||||
"get": "获取",
|
||||
"import": "导入",
|
||||
"inEffect": "生效中",
|
||||
"latestAnnouncement": "最新公告",
|
||||
@ -32,6 +37,7 @@
|
||||
"noPlanAvailable": "暂无套餐",
|
||||
"noReset": "不重置",
|
||||
"noYPlan": "尚未有套餐生效",
|
||||
"notEffect": "未生效",
|
||||
"online": "在线:",
|
||||
"pageOf": "第 {pageIndex} 页,共 {pageCount} 页",
|
||||
"pinnedAnnouncement": "【置顶公告】",
|
||||
@ -40,12 +46,14 @@
|
||||
"prompt": "提示",
|
||||
"purchaseSubscription": "购买订阅",
|
||||
"qrCode": "二维码",
|
||||
"quickDownloads": "快捷下载",
|
||||
"remaining": "剩余:",
|
||||
"resetSubscription": "重置订阅地址",
|
||||
"resetSuccess": "重置成功",
|
||||
"rowsPerPage": "每页显示",
|
||||
"scanCodeToSubscribe": "扫描码订阅",
|
||||
"scanToSubscribe": "扫描订阅",
|
||||
"selectOS": "选择对应操作系统下载客户端",
|
||||
"siteAnnouncements": "网站公告",
|
||||
"subscriptionUrl": "订阅地址",
|
||||
"totalTraffic": "总流量",
|
||||
|
||||
@ -13,7 +13,8 @@ const buttonVariants = cva(
|
||||
primary: 'bg-[#225BA9] text-primary-foreground shadow hover:bg-[#0F2C53] ',
|
||||
danger: 'bg-[#FF4248] text-primary-foreground shadow hover:bg-[#E22C2E]',
|
||||
dangerLink: 'text-[#E22C2E] ',
|
||||
primaryBlue: 'bg-[#A8D4ED] text-primary-foreground hover:bg-[#225BA9]',
|
||||
primaryBlue:
|
||||
'bg-[#B5C9E2] text-[#225BA9] hover:bg-[#225BA9] hover:text-primary-foreground ',
|
||||
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
||||
outline:
|
||||
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||
|
||||
@ -28,7 +28,7 @@ export function DatePicker({
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<Popover modal>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant='outline'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user