feat: 更新骨架屏UI
This commit is contained in:
@@ -1,8 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* 首页骨架屏组件
|
* 首页骨架屏组件
|
||||||
* 模拟首页的 6 个功能卡片布局,减少白屏感知时间
|
* 精准模拟首页的深色科技风格布局,减少白屏感知时间
|
||||||
*
|
*
|
||||||
* 使用 Chakra UI 的 Skeleton 组件
|
* 关键特性:
|
||||||
|
* - ✅ 深色渐变背景(与实际页面一致)
|
||||||
|
* - ✅ 玻璃态透明卡片
|
||||||
|
* - ✅ 全屏 Hero 区域布局
|
||||||
|
* - ✅ Featured 卡片横向布局 + 黄色边框
|
||||||
|
* - ✅ 5 个常规卡片网格布局
|
||||||
*
|
*
|
||||||
* @module views/Home/components/HomePageSkeleton
|
* @module views/Home/components/HomePageSkeleton
|
||||||
*/
|
*/
|
||||||
@@ -16,7 +21,7 @@ import {
|
|||||||
SkeletonText,
|
SkeletonText,
|
||||||
VStack,
|
VStack,
|
||||||
HStack,
|
HStack,
|
||||||
useColorModeValue,
|
Flex,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -31,66 +36,176 @@ interface HomePageSkeletonProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 单个卡片骨架
|
// Featured 卡片骨架(横向布局)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
const FeatureCardSkeleton: React.FC<{ isFeatured?: boolean }> = ({ isFeatured = false }) => {
|
const FeaturedCardSkeleton: React.FC<{ speed?: number }> = ({ speed = 0.8 }) => {
|
||||||
const bg = useColorModeValue('white', 'gray.800');
|
|
||||||
const borderColor = useColorModeValue('gray.200', 'gray.700');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
bg={bg}
|
bg="transparent"
|
||||||
borderRadius="xl"
|
border="2px solid"
|
||||||
borderWidth="1px"
|
borderColor="yellow.400"
|
||||||
borderColor={borderColor}
|
borderRadius={{ base: '2xl', md: '3xl' }}
|
||||||
p={isFeatured ? 8 : 6}
|
overflow="hidden"
|
||||||
h={isFeatured ? '350px' : '280px'}
|
|
||||||
boxShadow={isFeatured ? 'xl' : 'md'}
|
|
||||||
position="relative"
|
position="relative"
|
||||||
|
shadow="2xl"
|
||||||
|
w="100%"
|
||||||
|
_before={{
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
background: 'linear-gradient(135deg, rgba(255, 215, 0, 0.1) 0%, rgba(255, 165, 0, 0.05) 100%)',
|
||||||
|
zIndex: 0,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<VStack align="start" spacing={4} h="full">
|
<Box p={{ base: 6, md: 8 }} position="relative" zIndex={1}>
|
||||||
{/* 图标骨架 */}
|
{/* 横向布局 */}
|
||||||
|
<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}>
|
||||||
|
{/* 图标骨架 */}
|
||||||
|
<Skeleton
|
||||||
|
height={{ base: '48px', md: '56px' }}
|
||||||
|
width={{ base: '48px', md: '56px' }}
|
||||||
|
borderRadius={{ base: 'lg', md: 'xl' }}
|
||||||
|
startColor="yellow.200"
|
||||||
|
endColor="yellow.400"
|
||||||
|
speed={speed}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 标题 + 描述 */}
|
||||||
|
<VStack align="start" spacing={{ base: 1, md: 2 }} flex={1}>
|
||||||
|
{/* 标题 + 徽章 */}
|
||||||
|
<HStack>
|
||||||
|
<Skeleton
|
||||||
|
height={{ base: '28px', md: '32px' }}
|
||||||
|
width="150px"
|
||||||
|
borderRadius="md"
|
||||||
|
startColor="whiteAlpha.300"
|
||||||
|
endColor="whiteAlpha.500"
|
||||||
|
speed={speed}
|
||||||
|
/>
|
||||||
|
<Skeleton
|
||||||
|
height="20px"
|
||||||
|
width="50px"
|
||||||
|
borderRadius="full"
|
||||||
|
startColor="yellow.200"
|
||||||
|
endColor="yellow.400"
|
||||||
|
speed={speed}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
{/* 描述 */}
|
||||||
|
<SkeletonText
|
||||||
|
mt="2"
|
||||||
|
noOfLines={2}
|
||||||
|
spacing="3"
|
||||||
|
skeletonHeight="2"
|
||||||
|
width="100%"
|
||||||
|
maxW={{ md: 'md' }}
|
||||||
|
startColor="whiteAlpha.200"
|
||||||
|
endColor="whiteAlpha.400"
|
||||||
|
speed={speed}
|
||||||
|
/>
|
||||||
|
</VStack>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{/* 右侧:按钮 */}
|
||||||
|
<Skeleton
|
||||||
|
height="44px"
|
||||||
|
width={{ base: '100%', md: '140px' }}
|
||||||
|
borderRadius="full"
|
||||||
|
startColor="yellow.300"
|
||||||
|
endColor="yellow.500"
|
||||||
|
flexShrink={0}
|
||||||
|
speed={speed}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 常规卡片骨架(纵向布局)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
const RegularCardSkeleton: React.FC<{ speed?: number }> = ({ speed = 0.8 }) => {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
bg="whiteAlpha.100"
|
||||||
|
backdropFilter="blur(10px)"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor="whiteAlpha.200"
|
||||||
|
borderRadius={{ base: 'xl', md: '2xl' }}
|
||||||
|
p={{ base: 5, md: 6 }}
|
||||||
|
minH={{ base: 'auto', md: '180px' }}
|
||||||
|
transition="all 0.3s ease"
|
||||||
|
>
|
||||||
|
<VStack spacing={{ base: 3, md: 4 }} align="start" h="100%">
|
||||||
|
{/* 图标 + 徽章 */}
|
||||||
|
<HStack>
|
||||||
|
<Skeleton
|
||||||
|
height={{ base: '40px', md: '48px' }}
|
||||||
|
width={{ base: '40px', md: '48px' }}
|
||||||
|
borderRadius="lg"
|
||||||
|
startColor="whiteAlpha.200"
|
||||||
|
endColor="whiteAlpha.400"
|
||||||
|
speed={speed}
|
||||||
|
/>
|
||||||
|
<Skeleton
|
||||||
|
height="20px"
|
||||||
|
width="50px"
|
||||||
|
borderRadius="full"
|
||||||
|
startColor="whiteAlpha.200"
|
||||||
|
endColor="whiteAlpha.400"
|
||||||
|
speed={speed}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
{/* 标题 */}
|
||||||
<Skeleton
|
<Skeleton
|
||||||
height={isFeatured ? '60px' : '48px'}
|
height={{ base: '24px', md: '28px' }}
|
||||||
width={isFeatured ? '60px' : '48px'}
|
width="70%"
|
||||||
borderRadius="lg"
|
borderRadius="md"
|
||||||
startColor={isFeatured ? 'blue.100' : 'gray.100'}
|
startColor="whiteAlpha.300"
|
||||||
endColor={isFeatured ? 'blue.200' : 'gray.200'}
|
endColor="whiteAlpha.500"
|
||||||
|
speed={speed}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 标题骨架 */}
|
{/* 描述 */}
|
||||||
<Skeleton height="28px" width="70%" borderRadius="md" />
|
|
||||||
|
|
||||||
{/* 描述骨架 */}
|
|
||||||
<SkeletonText
|
<SkeletonText
|
||||||
mt="2"
|
mt="2"
|
||||||
noOfLines={isFeatured ? 4 : 3}
|
noOfLines={2}
|
||||||
spacing="3"
|
spacing="3"
|
||||||
skeletonHeight="2"
|
skeletonHeight="2"
|
||||||
width="100%"
|
width="100%"
|
||||||
|
startColor="whiteAlpha.200"
|
||||||
|
endColor="whiteAlpha.400"
|
||||||
|
speed={speed}
|
||||||
|
flex={1}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 按钮骨架 */}
|
{/* 按钮 */}
|
||||||
<Skeleton
|
<Skeleton
|
||||||
height="40px"
|
height="44px"
|
||||||
width={isFeatured ? '140px' : '100px'}
|
width={{ base: '100%', md: 'auto' }}
|
||||||
|
minW={{ md: '80px' }}
|
||||||
borderRadius="md"
|
borderRadius="md"
|
||||||
mt="auto"
|
alignSelf={{ base: 'stretch', md: 'flex-end' }}
|
||||||
|
startColor="whiteAlpha.200"
|
||||||
|
endColor="whiteAlpha.400"
|
||||||
|
speed={speed}
|
||||||
/>
|
/>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|
||||||
{/* Featured 徽章骨架 */}
|
|
||||||
{isFeatured && (
|
|
||||||
<Skeleton
|
|
||||||
position="absolute"
|
|
||||||
top="4"
|
|
||||||
right="4"
|
|
||||||
height="24px"
|
|
||||||
width="80px"
|
|
||||||
borderRadius="full"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -103,84 +218,58 @@ export const HomePageSkeleton: React.FC<HomePageSkeletonProps> = ({
|
|||||||
isAnimated = true,
|
isAnimated = true,
|
||||||
speed = 0.8,
|
speed = 0.8,
|
||||||
}) => {
|
}) => {
|
||||||
const containerBg = useColorModeValue('gray.50', 'gray.900');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
w="full"
|
w="full"
|
||||||
minH="100vh"
|
minH="100vh"
|
||||||
bg={containerBg}
|
bg="linear-gradient(135deg, #0E0C15 0%, #15131D 50%, #252134 100%)"
|
||||||
pt={{ base: '120px', md: '140px' }}
|
overflow="hidden"
|
||||||
pb={{ base: '60px', md: '80px' }}
|
position="relative"
|
||||||
>
|
>
|
||||||
<Container maxW="container.xl">
|
<Container maxW="7xl" position="relative" zIndex={30} px={{ base: 10, md: 10, lg: 10 }}>
|
||||||
<VStack spacing={{ base: 8, md: 12 }} align="stretch">
|
<VStack
|
||||||
{/* 顶部标题区域骨架 */}
|
spacing={{ base: 8, md: 12, lg: 16 }}
|
||||||
<VStack spacing={4} textAlign="center">
|
align="stretch"
|
||||||
|
minH="100vh"
|
||||||
|
justify="center"
|
||||||
|
>
|
||||||
|
{/* 主标题区域骨架 */}
|
||||||
|
<VStack spacing={{ base: 4, md: 5, lg: 6 }} textAlign="center" pt={{ base: 4, md: 6, lg: 8 }}>
|
||||||
{/* 主标题 */}
|
{/* 主标题 */}
|
||||||
<Skeleton
|
<Skeleton
|
||||||
height={{ base: '40px', md: '56px' }}
|
height={{ base: '40px', md: '56px', lg: '64px' }}
|
||||||
width={{ base: '80%', md: '500px' }}
|
width={{ base: '80%', md: '500px', lg: '600px' }}
|
||||||
borderRadius="md"
|
borderRadius="md"
|
||||||
|
startColor="whiteAlpha.300"
|
||||||
|
endColor="whiteAlpha.500"
|
||||||
speed={speed}
|
speed={speed}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 副标题 */}
|
{/* 副标题 */}
|
||||||
<Skeleton
|
<Skeleton
|
||||||
height={{ base: '20px', md: '24px' }}
|
height={{ base: '20px', md: '24px', lg: '28px' }}
|
||||||
width={{ base: '90%', md: '600px' }}
|
width={{ base: '90%', md: '600px', lg: '700px' }}
|
||||||
borderRadius="md"
|
borderRadius="md"
|
||||||
|
startColor="whiteAlpha.200"
|
||||||
|
endColor="whiteAlpha.400"
|
||||||
speed={speed}
|
speed={speed}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* CTA 按钮 */}
|
|
||||||
<HStack spacing={4} mt={4}>
|
|
||||||
<Skeleton
|
|
||||||
height="48px"
|
|
||||||
width="140px"
|
|
||||||
borderRadius="lg"
|
|
||||||
speed={speed}
|
|
||||||
/>
|
|
||||||
<Skeleton
|
|
||||||
height="48px"
|
|
||||||
width="140px"
|
|
||||||
borderRadius="lg"
|
|
||||||
speed={speed}
|
|
||||||
/>
|
|
||||||
</HStack>
|
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|
||||||
{/* 功能卡片网格骨架 */}
|
{/* 核心功能面板 */}
|
||||||
<SimpleGrid
|
<Box pb={{ base: 8, md: 12 }}>
|
||||||
columns={{ base: 1, md: 2, lg: 3 }}
|
<VStack spacing={{ base: 6, md: 8 }}>
|
||||||
spacing={{ base: 6, md: 8 }}
|
{/* Featured 卡片 - 新闻中心(横向布局) */}
|
||||||
mt={8}
|
<FeaturedCardSkeleton speed={speed} />
|
||||||
>
|
|
||||||
{/* 第一张卡片 - Featured (新闻中心) */}
|
|
||||||
<Box gridColumn={{ base: 'span 1', lg: 'span 2' }}>
|
|
||||||
<FeatureCardSkeleton isFeatured />
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* 其余 5 张卡片 */}
|
{/* 其他 5 个功能卡片 */}
|
||||||
{[1, 2, 3, 4, 5].map((index) => (
|
<SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={{ base: 4, md: 5, lg: 6 }} w="100%">
|
||||||
<FeatureCardSkeleton key={index} />
|
{[1, 2, 3, 4, 5].map((index) => (
|
||||||
))}
|
<RegularCardSkeleton key={index} speed={speed} />
|
||||||
</SimpleGrid>
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
{/* 底部装饰元素骨架 */}
|
</VStack>
|
||||||
<HStack justify="center" spacing={8} mt={12}>
|
</Box>
|
||||||
{[1, 2, 3].map((index) => (
|
|
||||||
<VStack key={index} spacing={2} align="center">
|
|
||||||
<Skeleton
|
|
||||||
height="40px"
|
|
||||||
width="40px"
|
|
||||||
borderRadius="full"
|
|
||||||
speed={speed}
|
|
||||||
/>
|
|
||||||
<Skeleton height="16px" width="60px" borderRadius="md" speed={speed} />
|
|
||||||
</VStack>
|
|
||||||
))}
|
|
||||||
</HStack>
|
|
||||||
</VStack>
|
</VStack>
|
||||||
</Container>
|
</Container>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
Reference in New Issue
Block a user