feat(LoadingState): 新增骨架屏变体,优化加载体验
- LoadingState: 新增 variant 参数支持 spinner/skeleton 模式 - LoadingState: 新增 skeletonType 参数支持 grid/list 布局 - AnnouncementsPanel: 使用 list 骨架屏替代 spinner - DisclosureSchedulePanel: 使用 grid 骨架屏替代 spinner 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,7 @@ const AnnouncementsPanel: React.FC<AnnouncementsPanelProps> = ({ stockCode, isAc
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <LoadingState message="加载公告数据..." />;
|
||||
return <LoadingState variant="skeleton" skeletonType="list" skeletonCount={5} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -27,7 +27,7 @@ const DisclosureSchedulePanel: React.FC<DisclosureSchedulePanelProps> = ({ stock
|
||||
const { disclosureSchedule, loading } = useDisclosureData({ stockCode, enabled: isActive });
|
||||
|
||||
if (loading) {
|
||||
return <LoadingState message="加载披露日程..." />;
|
||||
return <LoadingState variant="skeleton" skeletonType="grid" skeletonCount={4} />;
|
||||
}
|
||||
|
||||
if (disclosureSchedule.length === 0) {
|
||||
|
||||
@@ -1,22 +1,110 @@
|
||||
// src/views/Company/components/CompanyOverview/BasicInfoTab/components/LoadingState.tsx
|
||||
// 复用的加载状态组件
|
||||
// 复用的加载状态组件 - 支持骨架屏
|
||||
|
||||
import React from "react";
|
||||
import { Center, VStack, Spinner, Text } from "@chakra-ui/react";
|
||||
import React, { memo } from "react";
|
||||
import {
|
||||
Center,
|
||||
VStack,
|
||||
Spinner,
|
||||
Text,
|
||||
Box,
|
||||
Skeleton,
|
||||
SimpleGrid,
|
||||
HStack,
|
||||
} from "@chakra-ui/react";
|
||||
import { THEME } from "../config";
|
||||
|
||||
// 骨架屏颜色配置
|
||||
const SKELETON_COLORS = {
|
||||
startColor: "rgba(26, 32, 44, 0.6)",
|
||||
endColor: "rgba(212, 175, 55, 0.2)",
|
||||
};
|
||||
|
||||
interface LoadingStateProps {
|
||||
message?: string;
|
||||
height?: string;
|
||||
/** 使用骨架屏模式(更好的视觉体验) */
|
||||
variant?: "spinner" | "skeleton";
|
||||
/** 骨架屏类型:grid(网格布局)或 list(列表布局) */
|
||||
skeletonType?: "grid" | "list";
|
||||
/** 骨架屏项目数量 */
|
||||
skeletonCount?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载状态组件(黑金主题)
|
||||
* 网格骨架屏(用于披露日程等)
|
||||
*/
|
||||
const LoadingState: React.FC<LoadingStateProps> = ({
|
||||
const GridSkeleton: React.FC<{ count: number }> = memo(({ count }) => (
|
||||
<SimpleGrid columns={{ base: 2, md: 4 }} spacing={3}>
|
||||
{Array.from({ length: count }).map((_, i) => (
|
||||
<Skeleton
|
||||
key={i}
|
||||
height="80px"
|
||||
borderRadius="md"
|
||||
{...SKELETON_COLORS}
|
||||
/>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
));
|
||||
|
||||
GridSkeleton.displayName = "GridSkeleton";
|
||||
|
||||
/**
|
||||
* 列表骨架屏(用于公告列表等)
|
||||
*/
|
||||
const ListSkeleton: React.FC<{ count: number }> = memo(({ count }) => (
|
||||
<VStack spacing={2} align="stretch">
|
||||
{Array.from({ length: count }).map((_, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
p={3}
|
||||
borderRadius="md"
|
||||
bg="rgba(26, 32, 44, 0.4)"
|
||||
border="1px solid"
|
||||
borderColor="rgba(212, 175, 55, 0.2)"
|
||||
>
|
||||
<HStack justify="space-between">
|
||||
<VStack align="start" spacing={2} flex={1}>
|
||||
<HStack>
|
||||
<Skeleton height="20px" width="60px" borderRadius="sm" {...SKELETON_COLORS} />
|
||||
<Skeleton height="16px" width="80px" borderRadius="sm" {...SKELETON_COLORS} />
|
||||
</HStack>
|
||||
<Skeleton height="18px" width="90%" borderRadius="sm" {...SKELETON_COLORS} />
|
||||
</VStack>
|
||||
<Skeleton height="32px" width="32px" borderRadius="md" {...SKELETON_COLORS} />
|
||||
</HStack>
|
||||
</Box>
|
||||
))}
|
||||
</VStack>
|
||||
));
|
||||
|
||||
ListSkeleton.displayName = "ListSkeleton";
|
||||
|
||||
/**
|
||||
* 加载状态组件(黑金主题)
|
||||
*
|
||||
* @param variant - "spinner"(默认)或 "skeleton"(骨架屏)
|
||||
* @param skeletonType - 骨架屏类型:"grid" 或 "list"
|
||||
*/
|
||||
const LoadingState: React.FC<LoadingStateProps> = memo(({
|
||||
message = "加载中...",
|
||||
height = "200px",
|
||||
variant = "spinner",
|
||||
skeletonType = "list",
|
||||
skeletonCount = 4,
|
||||
}) => {
|
||||
if (variant === "skeleton") {
|
||||
return (
|
||||
<Box minH={height} p={4}>
|
||||
{skeletonType === "grid" ? (
|
||||
<GridSkeleton count={skeletonCount} />
|
||||
) : (
|
||||
<ListSkeleton count={skeletonCount} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Center h={height}>
|
||||
<VStack>
|
||||
@@ -27,6 +115,8 @@ const LoadingState: React.FC<LoadingStateProps> = ({
|
||||
</VStack>
|
||||
</Center>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
LoadingState.displayName = "LoadingState";
|
||||
|
||||
export default LoadingState;
|
||||
|
||||
Reference in New Issue
Block a user