22301014 | 356527a | 2025-06-09 17:46:56 +0800 | [diff] [blame^] | 1 | import React, { useEffect, useMemo } from 'react'; |
| 2 | import { useDispatch } from 'react-redux'; |
| 3 | import { Input, Spin, Alert } from 'antd'; |
| 4 | import { SearchOutlined } from '@ant-design/icons'; |
| 5 | import type { Section, Artwork } from './types'; |
| 6 | import { initializeArtworks, selectFilteredArtworks, selectSearchTerm, selectWorkListError, selectWorkListLoading, setSearchTerm } from './workListSlice'; |
| 7 | import { useAppSelector } from '../../store/hooks'; |
| 8 | import { selectSections, selectCategoryLoading, selectCategoryError, initializeSections } from './categorySlice'; |
| 9 | import Category from './Category'; |
22301014 | 3d96630 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 10 | |
22301014 | 356527a | 2025-06-09 17:46:56 +0800 | [diff] [blame^] | 11 | const Home: React.FC = () => { |
| 12 | const dispatch = useDispatch(); |
22301014 | bc4616f | 2025-06-03 16:59:44 +0800 | [diff] [blame] | 13 | |
22301014 | 356527a | 2025-06-09 17:46:56 +0800 | [diff] [blame^] | 14 | // 从Redux store获取状态 |
| 15 | const sections = useAppSelector(selectSections); |
| 16 | const filteredArtworks = useAppSelector(selectFilteredArtworks); |
| 17 | const searchTerm = useAppSelector(selectSearchTerm); |
| 18 | const workListLoading = useAppSelector(selectWorkListLoading); |
| 19 | const workListError = useAppSelector(selectWorkListError); |
| 20 | const categoryLoading = useAppSelector(selectCategoryLoading); |
| 21 | const categoryError = useAppSelector(selectCategoryError); |
22301014 | 3d96630 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 22 | |
22301014 | 356527a | 2025-06-09 17:46:56 +0800 | [diff] [blame^] | 23 | // 综合加载状态和错误状态 |
| 24 | const loading = workListLoading || categoryLoading; |
| 25 | const error = workListError || categoryError; |
22301014 | 3d96630 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 26 | |
22301014 | 356527a | 2025-06-09 17:46:56 +0800 | [diff] [blame^] | 27 | // 示例数据 |
| 28 | const sampleSections: Section[] = [ |
| 29 | { |
| 30 | id: 1, |
| 31 | childrenid: [1, 2, 3], |
| 32 | name: "数字艺术" |
| 33 | }, |
| 34 | { |
| 35 | id: 2, |
| 36 | childrenid: [4, 5], |
| 37 | name: "传统绘画" |
| 38 | } |
| 39 | ]; |
22301014 | 3d96630 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 40 | |
22301014 | 356527a | 2025-06-09 17:46:56 +0800 | [diff] [blame^] | 41 | const sampleArtworks: Artwork[] = [ |
| 42 | { |
| 43 | id: 1, |
| 44 | title: "未来城市概念设计", |
| 45 | author: "视觉设计师张三", |
| 46 | views: 1247, |
| 47 | category: { |
| 48 | id: 1, |
| 49 | name: "数字艺术", |
| 50 | children: ["概念设计", "建筑设计"] |
| 51 | }, |
| 52 | description: "一个关于2050年智慧城市的概念设计作品,融合了可持续发展、人工智能和绿色科技的理念", |
| 53 | createTime: "2024-01-15T10:30:00Z", |
| 54 | cover: "https://picsum.photos/300/200?random=1" |
| 55 | }, |
| 56 | { |
| 57 | id: 2, |
| 58 | title: "移动应用界面设计套件", |
| 59 | author: "UI设计师李四", |
| 60 | views: 856, |
| 61 | category: { |
| 62 | id: 2, |
| 63 | name: "界面设计", |
| 64 | children: ["UI/UX设计", "移动端设计"] |
| 65 | }, |
| 66 | description: "一套完整的移动端UI设计规范和组件库,包含100+个精美界面和500+个设计组件", |
| 67 | createTime: "2024-02-20T14:15:00Z", |
| 68 | cover: "https://picsum.photos/300/200?random=2" |
| 69 | }, |
| 70 | { |
| 71 | id: 3, |
| 72 | title: "React组件库开发指南", |
| 73 | author: "刘松林", |
| 74 | views: 432, |
| 75 | category: { |
| 76 | id: 3, |
| 77 | name: "程序开发", |
| 78 | children: ["前端开发", "React技术"] |
| 79 | }, |
| 80 | description: "一套完整的企业级React组件库开发教程和源码,包含从设计到发布的完整流程", |
| 81 | createTime: "2024-03-10T09:45:00Z", |
| 82 | cover: "https://picsum.photos/300/200?random=3" |
| 83 | }, |
| 84 | { |
| 85 | id: 4, |
| 86 | title: "机械战士3D模型", |
| 87 | author: "3D艺术家王五", |
| 88 | views: 789, |
| 89 | category: { |
| 90 | id: 1, |
| 91 | name: "数字艺术", |
| 92 | children: ["3D建模", "科幻设计"] |
| 93 | }, |
| 94 | description: "一个高精度的科幻机械战士3D模型,包含完整的材质贴图和动画骨骼系统", |
| 95 | createTime: "2024-01-25T16:20:00Z", |
| 96 | cover: "https://picsum.photos/300/200?random=4" |
| 97 | }, |
| 98 | { |
| 99 | id: 5, |
| 100 | title: "城市夜景摄影集", |
| 101 | author: "摄影师赵六", |
| 102 | views: 1123, |
| 103 | category: { |
| 104 | id: 4, |
| 105 | name: "摄影艺术", |
| 106 | children: ["摄影作品", "城市风光"] |
| 107 | }, |
| 108 | description: "一组精美的城市夜景摄影作品,捕捉了都市夜晚的璀璨光影", |
| 109 | createTime: "2024-02-14T11:30:00Z", |
| 110 | cover: "https://picsum.photos/300/200?random=5" |
| 111 | }, |
| 112 | { |
| 113 | id: 6, |
| 114 | title: "奇幻世界插画系列", |
| 115 | author: "插画师孙七", |
| 116 | views: 945, |
| 117 | category: { |
| 118 | id: 5, |
| 119 | name: "插画艺术", |
| 120 | children: ["插画艺术", "奇幻风格"] |
| 121 | }, |
| 122 | description: "一套充满想象力的奇幻题材插画作品,包含角色设计、场景概念图和完整插图", |
| 123 | createTime: "2024-03-05T13:20:00Z", |
| 124 | cover: "https://picsum.photos/300/200?random=6" |
| 125 | } |
| 126 | ]; |
22301014 | 3d96630 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 127 | |
22301014 | 356527a | 2025-06-09 17:46:56 +0800 | [diff] [blame^] | 128 | // 初始化数据 |
| 129 | useEffect(() => { |
| 130 | dispatch(initializeArtworks(sampleArtworks)); |
| 131 | dispatch(initializeSections(sampleSections)); |
| 132 | }, [dispatch]); |
| 133 | |
| 134 | // 处理搜索输入 |
| 135 | const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
| 136 | dispatch(setSearchTerm(e.target.value)); |
| 137 | }; |
| 138 | |
| 139 | // 根据分区ID获取对应的作品 |
| 140 | const getArtworksBySection = useMemo(() => { |
| 141 | return (childrenIds: number[]): Artwork[] => { |
| 142 | return filteredArtworks.filter(artwork => childrenIds.includes(artwork.id)); |
| 143 | }; |
| 144 | }, [filteredArtworks]); |
| 145 | |
| 146 | // 渲染加载状态 |
| 147 | if (loading) { |
| 148 | return ( |
| 149 | <div style={{ |
| 150 | display: 'flex', |
| 151 | justifyContent: 'center', |
| 152 | alignItems: 'center', |
| 153 | height: '100vh' |
| 154 | }}> |
| 155 | <Spin size="large" tip="加载中..." /> |
| 156 | </div> |
| 157 | ); |
| 158 | } |
| 159 | |
| 160 | // 渲染错误状态 |
| 161 | if (error) { |
| 162 | return ( |
| 163 | <div style={{ padding: '24px' }}> |
| 164 | <Alert |
| 165 | message="加载失败" |
| 166 | description={error} |
| 167 | type="error" |
| 168 | showIcon |
| 169 | /> |
| 170 | </div> |
| 171 | ); |
| 172 | } |
| 173 | |
| 174 | return ( |
| 175 | <div style={{ |
| 176 | backgroundColor: '#f5f5f5', |
| 177 | minHeight: '100vh', |
| 178 | padding: '24px' |
| 179 | }}> |
| 180 | <div style={{ maxWidth: 1200, margin: '0 auto' }}> |
| 181 | {/* 搜索栏 */} |
| 182 | <div style={{ marginBottom: '32px' }}> |
| 183 | <Input |
| 184 | size="large" |
| 185 | placeholder="搜索作品标题、作者、分类..." |
| 186 | prefix={<SearchOutlined />} |
| 187 | value={searchTerm} |
| 188 | onChange={handleSearchChange} |
| 189 | style={{ maxWidth: 600, width: '100%' }} |
| 190 | allowClear |
| 191 | /> |
22301014 | 3d96630 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 192 | </div> |
22301014 | 3d96630 | 2025-06-07 22:54:40 +0800 | [diff] [blame] | 193 | |
22301014 | 356527a | 2025-06-09 17:46:56 +0800 | [diff] [blame^] | 194 | {/* 分区展示 */} |
| 195 | {sections.map((section) => { |
| 196 | const sectionArtworks = getArtworksBySection(section.childrenid); |
22301014 | bc4616f | 2025-06-03 16:59:44 +0800 | [diff] [blame] | 197 | |
22301014 | 356527a | 2025-06-09 17:46:56 +0800 | [diff] [blame^] | 198 | return ( |
| 199 | <Category |
| 200 | key={section.id} |
| 201 | section={section} |
| 202 | artworks={sectionArtworks} |
| 203 | /> |
| 204 | ); |
| 205 | })} |
| 206 | |
| 207 | {/* 无搜索结果提示 */} |
| 208 | {searchTerm && filteredArtworks.length === 0 && ( |
| 209 | <div style={{ |
| 210 | textAlign: 'center', |
| 211 | padding: '48px 0', |
| 212 | color: '#999' |
| 213 | }}> |
| 214 | <p style={{ fontSize: 16 }}>未找到相关作品</p> |
| 215 | <p>尝试使用其他关键词搜索</p> |
| 216 | </div> |
| 217 | )} |
| 218 | </div> |
| 219 | </div> |
| 220 | ); |
| 221 | }; |
| 222 | |
| 223 | export default Home; |