feat: 修复首页新闻中心卡片布局跳变问题
问题根源:
使用 useBreakpointValue 的 isMobile 变量在初始渲染时返回 undefined,导致:
1. 服务端渲染/首次加载时显示一种布局
2. 客户端水合后切换到另一种布局
3. 用户看到明显的布局跳变(先横向后纵向,或反之)
解决方案:
不使用条件渲染两套完全不同的 JSX,而是使用响应式样式让同一套 JSX 自动适应不同屏幕。
修改策略:
将移动端(VStack)和桌面端(Flex横向)合并为一套响应式布局:
- 使用 Flex + 响应式 flexDirection
- flexDirection={{ base: column, md: row }}(移动端纵向,桌面端横向)
- 统一使用响应式属性而不是条件渲染
This commit is contained in:
@@ -34,7 +34,6 @@ export default function HomePage() {
|
||||
const heroTextSize = useBreakpointValue({ base: 'md', md: 'lg', lg: 'xl' });
|
||||
const containerPx = useBreakpointValue({ base: 10, md: 10, lg: 10 });
|
||||
const showDecorations = useBreakpointValue({ base: false, md: true });
|
||||
const isMobile = useBreakpointValue({ base: true, md: false });
|
||||
|
||||
// 保留原有的调试信息
|
||||
useEffect(() => {
|
||||
@@ -238,79 +237,49 @@ export default function HomePage() {
|
||||
}}
|
||||
>
|
||||
<CardBody p={{ base: 6, md: 8 }} position="relative" zIndex={1}>
|
||||
{isMobile ? (
|
||||
/* 移动端:垂直布局 */
|
||||
<VStack spacing={4} align="stretch">
|
||||
<HStack spacing={4}>
|
||||
{/* 响应式布局:移动端纵向,桌面端横向 */}
|
||||
<Flex
|
||||
direction={{ base: 'column', md: 'row' }}
|
||||
align={{ base: 'stretch', md: 'center' }}
|
||||
justify={{ base: 'flex-start', md: 'space-between' }}
|
||||
gap={{ base: 4, md: 6 }}
|
||||
>
|
||||
<Flex align="center" gap={{ base: 4, md: 6 }} flex={1}>
|
||||
<Box
|
||||
p={3}
|
||||
borderRadius="lg"
|
||||
p={{ base: 3, md: 4 }}
|
||||
borderRadius={{ base: 'lg', md: 'xl' }}
|
||||
bg="yellow.400"
|
||||
color="black"
|
||||
>
|
||||
<Text fontSize="2xl">{coreFeatures[0].icon}</Text>
|
||||
<Text fontSize={{ base: '2xl', md: '3xl' }}>{coreFeatures[0].icon}</Text>
|
||||
</Box>
|
||||
<VStack align="start" spacing={1} flex={1}>
|
||||
<Heading size="lg" color="white">
|
||||
<VStack align="start" spacing={{ base: 1, md: 2 }} flex={1}>
|
||||
<HStack>
|
||||
<Heading size={{ base: 'lg', md: 'xl' }} color="white">
|
||||
{coreFeatures[0].title}
|
||||
</Heading>
|
||||
<Badge colorScheme="yellow" variant="solid" fontSize="xs">
|
||||
<Badge colorScheme="yellow" variant="solid" fontSize={{ base: 'xs', md: 'sm' }}>
|
||||
{coreFeatures[0].badge}
|
||||
</Badge>
|
||||
</VStack>
|
||||
</HStack>
|
||||
<Text color="whiteAlpha.800" fontSize="md" lineHeight="tall">
|
||||
<Text color="whiteAlpha.800" fontSize={{ base: 'md', md: 'lg' }} maxW={{ md: 'md' }} lineHeight="tall">
|
||||
{coreFeatures[0].description}
|
||||
</Text>
|
||||
</VStack>
|
||||
</Flex>
|
||||
<Button
|
||||
colorScheme="yellow"
|
||||
size="md"
|
||||
size={{ base: 'md', md: 'lg' }}
|
||||
borderRadius="full"
|
||||
fontWeight="bold"
|
||||
w="100%"
|
||||
w={{ base: '100%', md: 'auto' }}
|
||||
onClick={() => handleProductClick(coreFeatures[0].url)}
|
||||
minH="44px"
|
||||
>
|
||||
进入功能 →
|
||||
</Button>
|
||||
</VStack>
|
||||
) : (
|
||||
/* 桌面端:横向布局 */
|
||||
<Flex align="center" justify="space-between">
|
||||
<HStack spacing={6}>
|
||||
<Box
|
||||
p={4}
|
||||
borderRadius="xl"
|
||||
bg="yellow.400"
|
||||
color="black"
|
||||
>
|
||||
<Text fontSize="3xl">{coreFeatures[0].icon}</Text>
|
||||
</Box>
|
||||
<VStack align="start" spacing={2}>
|
||||
<HStack>
|
||||
<Heading size="xl" color="white">
|
||||
{coreFeatures[0].title}
|
||||
</Heading>
|
||||
<Badge colorScheme="yellow" variant="solid" fontSize="sm">
|
||||
{coreFeatures[0].badge}
|
||||
</Badge>
|
||||
</HStack>
|
||||
<Text color="whiteAlpha.800" fontSize="lg" maxW="md">
|
||||
{coreFeatures[0].description}
|
||||
</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
<Button
|
||||
colorScheme="yellow"
|
||||
size="lg"
|
||||
borderRadius="full"
|
||||
fontWeight="bold"
|
||||
onClick={() => handleProductClick(coreFeatures[0].url)}
|
||||
flexShrink={0}
|
||||
>
|
||||
进入功能 →
|
||||
</Button>
|
||||
</Flex>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user