feat: 联调充值和提现接口
This commit is contained in:
180
src/components/ui/notification-alert.tsx
Normal file
180
src/components/ui/notification-alert.tsx
Normal file
@@ -0,0 +1,180 @@
|
||||
import {
|
||||
CheckCircle2,
|
||||
Info,
|
||||
LoaderCircle,
|
||||
TriangleAlert,
|
||||
XCircle,
|
||||
} from 'lucide-react'
|
||||
import { motion } from 'motion/react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import {
|
||||
NOTIFICATION_EXIT_DURATION_MS,
|
||||
useNotificationStore,
|
||||
} from '@/lib/notify'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const TONE_CLASS_BY_TYPE = {
|
||||
error: {
|
||||
alert:
|
||||
'border-[#E37284]/38 bg-[linear-gradient(180deg,rgba(46,11,18,0.96),rgba(23,7,11,0.98))] shadow-[0_calc(var(--design-unit)*18)_calc(var(--design-unit)*44)_rgba(118,18,40,0.34)]',
|
||||
glow: 'from-[#FF7D93]/20 via-[#FF7D93]/8 to-transparent',
|
||||
icon: <XCircle className="h-design-18 w-design-18 text-[#FFB2BE]" />,
|
||||
iconShell:
|
||||
'border-[rgba(255,154,168,0.26)] bg-[linear-gradient(180deg,rgba(124,34,46,0.42),rgba(76,19,29,0.3))] shadow-[inset_0_1px_0_rgba(255,224,228,0.16)]',
|
||||
line: 'bg-[#FF7D93]',
|
||||
title: 'text-[#FFF5F7]',
|
||||
},
|
||||
info: {
|
||||
alert:
|
||||
'border-[rgba(114,226,243,0.34)] bg-[linear-gradient(180deg,rgba(5,30,43,0.96),rgba(4,17,26,0.98))] shadow-[0_calc(var(--design-unit)*18)_calc(var(--design-unit)*44)_rgba(9,78,108,0.34)]',
|
||||
glow: 'from-[#6FEAFF]/20 via-[#6FEAFF]/8 to-transparent',
|
||||
icon: <Info className="h-design-18 w-design-18 text-[#A5F3FF]" />,
|
||||
iconShell:
|
||||
'border-[rgba(121,228,238,0.24)] bg-[linear-gradient(180deg,rgba(17,85,98,0.38),rgba(10,53,63,0.28))] shadow-[inset_0_1px_0_rgba(227,252,255,0.14)]',
|
||||
line: 'bg-[#73ECFF]',
|
||||
title: 'text-[#F2FEFF]',
|
||||
},
|
||||
loading: {
|
||||
alert:
|
||||
'border-[rgba(114,226,243,0.34)] bg-[linear-gradient(180deg,rgba(5,30,43,0.96),rgba(4,17,26,0.98))] shadow-[0_calc(var(--design-unit)*18)_calc(var(--design-unit)*44)_rgba(9,78,108,0.34)]',
|
||||
glow: 'from-[#6FEAFF]/20 via-[#6FEAFF]/8 to-transparent',
|
||||
icon: (
|
||||
<LoaderCircle className="h-design-18 w-design-18 animate-spin text-[#A5F3FF]" />
|
||||
),
|
||||
iconShell:
|
||||
'border-[rgba(121,228,238,0.24)] bg-[linear-gradient(180deg,rgba(17,85,98,0.38),rgba(10,53,63,0.28))] shadow-[inset_0_1px_0_rgba(227,252,255,0.14)]',
|
||||
line: 'bg-[#73ECFF]',
|
||||
title: 'text-[#F2FEFF]',
|
||||
},
|
||||
success: {
|
||||
alert:
|
||||
'border-[rgba(121,229,171,0.34)] bg-[linear-gradient(180deg,rgba(8,36,29,0.96),rgba(5,18,14,0.98))] shadow-[0_calc(var(--design-unit)*18)_calc(var(--design-unit)*44)_rgba(12,89,53,0.34)]',
|
||||
glow: 'from-[#73F0B0]/18 via-[#73F0B0]/7 to-transparent',
|
||||
icon: <CheckCircle2 className="h-design-18 w-design-18 text-[#B8FFD8]" />,
|
||||
iconShell:
|
||||
'border-[rgba(127,236,177,0.24)] bg-[linear-gradient(180deg,rgba(24,98,66,0.38),rgba(15,60,40,0.28))] shadow-[inset_0_1px_0_rgba(232,255,242,0.14)]',
|
||||
line: 'bg-[#73F0B0]',
|
||||
title: 'text-[#F4FFF8]',
|
||||
},
|
||||
warning: {
|
||||
alert:
|
||||
'border-[rgba(239,197,103,0.36)] bg-[linear-gradient(180deg,rgba(48,31,10,0.96),rgba(25,17,7,0.98))] shadow-[0_calc(var(--design-unit)*18)_calc(var(--design-unit)*44)_rgba(109,72,12,0.34)]',
|
||||
glow: 'from-[#FFD66E]/20 via-[#FFD66E]/8 to-transparent',
|
||||
icon: <TriangleAlert className="h-design-18 w-design-18 text-[#FFE4A0]" />,
|
||||
iconShell:
|
||||
'border-[rgba(255,214,110,0.24)] bg-[linear-gradient(180deg,rgba(110,77,26,0.38),rgba(66,46,15,0.28))] shadow-[inset_0_1px_0_rgba(255,247,223,0.14)]',
|
||||
line: 'bg-[#FFD66E]',
|
||||
title: 'text-[#FFFAEF]',
|
||||
},
|
||||
} as const
|
||||
|
||||
export function AppNotificationAlert() {
|
||||
const activeDialog = useNotificationStore((state) => state.activeDialog)
|
||||
const closingDialogId = useNotificationStore((state) => state.closingDialogId)
|
||||
|
||||
if (!activeDialog || typeof document === 'undefined') {
|
||||
return null
|
||||
}
|
||||
|
||||
const tone = TONE_CLASS_BY_TYPE[activeDialog.type]
|
||||
const isClosing = closingDialogId === activeDialog.id
|
||||
|
||||
const motionState = isClosing ? 'closing' : 'visible'
|
||||
const motionVariants = {
|
||||
closing: {
|
||||
filter: 'blur(1px)',
|
||||
opacity: 0,
|
||||
scale: 0.985,
|
||||
y: -8,
|
||||
transition: {
|
||||
duration: NOTIFICATION_EXIT_DURATION_MS / 1000,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
},
|
||||
},
|
||||
hidden: {
|
||||
filter: 'blur(3px)',
|
||||
opacity: 0,
|
||||
scale: 0.94,
|
||||
y: -18,
|
||||
},
|
||||
visible: {
|
||||
filter: 'blur(0px)',
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
type: 'spring',
|
||||
damping: 26,
|
||||
mass: 0.72,
|
||||
stiffness: 360,
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
return createPortal(
|
||||
<div className="pointer-events-none fixed inset-x-0 top-[calc(var(--design-unit)*52)] z-[10000] flex justify-center px-4">
|
||||
<motion.div
|
||||
key={activeDialog.id}
|
||||
initial="hidden"
|
||||
animate={motionState}
|
||||
variants={motionVariants}
|
||||
className="w-[min(92vw,calc(var(--design-unit)*468))] will-change-transform"
|
||||
>
|
||||
<Alert
|
||||
className={cn(
|
||||
'relative origin-top flex w-full max-w-none items-center gap-design-12 overflow-hidden rounded-[calc(var(--design-unit)*14)] border px-design-14 py-design-12 text-left backdrop-blur-xl',
|
||||
tone.alert,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
'absolute inset-x-0 top-0 h-[1px] bg-gradient-to-r from-transparent via-white/24 to-transparent',
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
'absolute top-design-10 bottom-design-10 left-design-10 w-[calc(var(--design-unit)*3)] rounded-full opacity-90 shadow-[0_0_calc(var(--design-unit)*14)_currentColor]',
|
||||
tone.line,
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
'absolute inset-0 bg-gradient-to-r opacity-100',
|
||||
tone.glow,
|
||||
)}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'relative z-10 flex h-design-38 w-design-38 shrink-0 items-center justify-center rounded-[calc(var(--design-unit)*11)] border',
|
||||
tone.iconShell,
|
||||
)}
|
||||
>
|
||||
{tone.icon}
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex min-h-design-38 min-w-0 flex-1 flex-col justify-center">
|
||||
<AlertTitle
|
||||
className={cn(
|
||||
'text-design-15 font-semibold leading-[1.25] text-shadow-[0_1px_0_rgba(0,0,0,0.18)]',
|
||||
tone.title,
|
||||
)}
|
||||
>
|
||||
{activeDialog.message}
|
||||
</AlertTitle>
|
||||
{activeDialog.description ? (
|
||||
<AlertDescription className="pt-design-3 whitespace-pre-line text-design-12 leading-[1.45] text-white/62">
|
||||
{activeDialog.description}
|
||||
</AlertDescription>
|
||||
) : null}
|
||||
</div>
|
||||
</Alert>
|
||||
</motion.div>
|
||||
</div>,
|
||||
document.body,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user