update pay ui

This commit is contained in:
2025-12-02 08:07:46 +08:00
parent 02d5311005
commit 2395d92b17
2 changed files with 1264 additions and 56 deletions

1176
concept_hierarchy.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -22,6 +22,7 @@ import {
Input, Input,
Icon, Icon,
Container, Container,
useBreakpointValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
FaWeixin, FaWeixin,
@@ -42,6 +43,87 @@ import { useAuth } from '../../contexts/AuthContext';
import { useSubscriptionEvents } from '../../hooks/useSubscriptionEvents'; import { useSubscriptionEvents } from '../../hooks/useSubscriptionEvents';
import { subscriptionConfig, themeColors } from '../../views/Pages/Account/subscription-content'; import { subscriptionConfig, themeColors } from '../../views/Pages/Account/subscription-content';
// 计费周期选择器组件 - 移动端垂直布局(年付在上),桌面端水平布局
interface CycleSelectorProps {
options: any[];
selectedCycle: string;
onSelectCycle: (cycle: string) => void;
}
function CycleSelector({ options, selectedCycle, onSelectCycle }: CycleSelectorProps) {
// 使用 useBreakpointValue 动态获取是否是移动端
const isMobile = useBreakpointValue({ base: true, md: false });
// 移动端倒序显示(年付在上),桌面端正常顺序
const displayOptions = isMobile ? [...options].reverse() : options;
return (
<Flex
direction={{ base: 'column', md: 'row' }}
gap={3}
p={2}
bg="rgba(255, 255, 255, 0.03)"
borderRadius="xl"
border="1px solid"
borderColor="rgba(255, 255, 255, 0.1)"
backdropFilter="blur(10px)"
justify="center"
align="center"
w={{ base: 'full', md: 'auto' }}
maxW={{ base: '320px', md: 'none' }}
mx="auto"
>
{displayOptions.map((option: any) => (
<Box key={option.cycleKey} position="relative" w={{ base: 'full', md: 'auto' }}>
{option.discountPercent > 0 && (
<Badge
position="absolute"
top={{ base: '50%', md: '-10px' }}
right={{ base: '10px', md: '-10px' }}
transform={{ base: 'translateY(-50%)', md: 'none' }}
colorScheme="red"
fontSize="xs"
px={2}
py={1}
borderRadius="full"
fontWeight="bold"
zIndex={1}
>
{option.discountPercent}%
</Badge>
)}
<Button
size="lg"
w={{ base: 'full', md: 'auto' }}
px={6}
py={6}
borderRadius="lg"
bg={selectedCycle === option.cycleKey ? 'linear-gradient(135deg, #D4AF37, #B8941F)' : 'transparent'}
color={selectedCycle === option.cycleKey ? '#000' : '#fff'}
border="1px solid"
borderColor={selectedCycle === option.cycleKey ? 'rgba(212, 175, 55, 0.3)' : 'rgba(255, 255, 255, 0.1)'}
onClick={() => onSelectCycle(option.cycleKey)}
_hover={{
transform: 'translateY(-2px)',
borderColor: 'rgba(212, 175, 55, 0.5)',
shadow: selectedCycle === option.cycleKey
? '0 0 20px rgba(212, 175, 55, 0.3)'
: '0 4px 12px rgba(0, 0, 0, 0.5)',
}}
transition="all 0.3s"
fontWeight="bold"
justifyContent={{ base: 'flex-start', md: 'center' }}
pl={{ base: 6, md: 6 }}
>
{option.label}
</Button>
</Box>
))}
</Flex>
);
}
export default function SubscriptionContentNew() { export default function SubscriptionContentNew() {
const { user } = useAuth(); const { user } = useAuth();
const subscriptionEvents = useSubscriptionEvents({ const subscriptionEvents = useSubscriptionEvents({
@@ -51,7 +133,7 @@ export default function SubscriptionContentNew() {
}, },
}); });
const [selectedCycle, setSelectedCycle] = useState('monthly'); const [selectedCycle, setSelectedCycle] = useState('yearly');
const [selectedPlan, setSelectedPlan] = useState(null); const [selectedPlan, setSelectedPlan] = useState(null);
const [subscriptionPlans, setSubscriptionPlans] = useState([]); const [subscriptionPlans, setSubscriptionPlans] = useState([]);
const [priceInfo, setPriceInfo] = useState(null); const [priceInfo, setPriceInfo] = useState(null);
@@ -751,61 +833,11 @@ export default function SubscriptionContentNew() {
· ·
</Text> </Text>
<HStack <CycleSelector
spacing={3} options={getMergedPlans()[1]?.pricingOptions || []}
p={2} selectedCycle={selectedCycle}
bg="rgba(255, 255, 255, 0.03)" onSelectCycle={setSelectedCycle}
borderRadius="xl" />
border="1px solid"
borderColor="rgba(255, 255, 255, 0.1)"
backdropFilter="blur(10px)"
flexWrap="wrap"
justify="center"
>
{getMergedPlans()[1]?.pricingOptions?.map((option: any, index: number) => (
<Box key={index} position="relative">
{option.discountPercent > 0 && (
<Badge
position="absolute"
top="-10px"
right="-10px"
colorScheme="red"
fontSize="xs"
px={2}
py={1}
borderRadius="full"
fontWeight="bold"
zIndex={1}
>
{option.discountPercent}%
</Badge>
)}
<Button
size="lg"
px={6}
py={6}
borderRadius="lg"
bg={selectedCycle === option.cycleKey ? 'linear-gradient(135deg, #D4AF37, #B8941F)' : 'transparent'}
color={selectedCycle === option.cycleKey ? '#000' : '#fff'}
border="1px solid"
borderColor={selectedCycle === option.cycleKey ? 'rgba(212, 175, 55, 0.3)' : 'rgba(255, 255, 255, 0.1)'}
onClick={() => setSelectedCycle(option.cycleKey)}
_hover={{
transform: 'translateY(-2px)',
borderColor: 'rgba(212, 175, 55, 0.5)',
shadow: selectedCycle === option.cycleKey
? '0 0 20px rgba(212, 175, 55, 0.3)'
: '0 4px 12px rgba(0, 0, 0, 0.5)',
}}
transition="all 0.3s"
fontWeight="bold"
>
{option.label}
</Button>
</Box>
))}
</HStack>
{(() => { {(() => {
const currentOption = getMergedPlans()[1]?.pricingOptions?.find( const currentOption = getMergedPlans()[1]?.pricingOptions?.find(