diff --git a/CLAUDE.md b/CLAUDE.md
index 16506c2..89bbac6 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -72,3 +72,58 @@ Pages must be registered in `src/app.config.ts` under the `pages` array.
- `src/app.ts` — class-based App component, renders `this.props.children` (the current page)
- `src/app.config.ts` — defines routes (`pages` array), window config, and app-level settings
- `src/index.html` — H5 entry HTML template
+
+## VantUI Component API Reference (v3.7)
+
+This project uses `@antmjs/vantui` v3.7. The official docs: https://antmjs.github.io/vantui/main/
+
+**Always check the actual type definitions** in `node_modules/@antmjs/vantui/types/` when unsure about props. Key API notes for commonly used components:
+
+### Search
+- `shape`: `'square'` (default) | `'round'`
+- `background`: background color string (default `#FFFFFF`)
+- `onFocus`, `onBlur`, `onChange`, `onSearch`, `onClear`, `onCancel` — event callbacks
+- `leftIcon`, `rightIcon` — icon name or image URL
+- `showAction` + `actionText` — right-side action button
+
+### Swiper (NOT the same as Taro's native Swiper)
+- `autoPlay` — auto-play interval in ms (0 = disabled). **Not** `autoplay`
+- `loop` — infinite loop mode. **Not** `circular`
+- `paginationColor` — indicator dot color. **Not** `indicatorColor`
+- `paginationVisible` — show/hide pagination dots
+- `duration` — animation duration (default 500)
+- `width` / `height` — card dimensions
+- **No** `indicatorActiveColor` prop — only one `paginationColor`
+- `onChange` callback: `(currPage: number) => void` (not an event object)
+
+### SwiperItem
+- Minimal component, only `className` and `children`. No special props.
+
+### Grid / GridItem
+- `Grid`: `columnNum`, `border`, `gutter`, `square`, `clickable`, `center`, `iconSize`
+- `GridItem`: `icon` (string), `text` (ReactNode), `iconColor`, `dot`, `info`/`badge`, `url` + `linkType`
+
+### Tabs / Tab (v3.7)
+- `Tabs`:
+ - `active` — current tab index (number | string)
+ - `sticky` — enable sticky mode
+ - `offsetTop` — sticky offset in px. **Not** `stickyOffsetTop`
+ - `color`, `titleActiveColor`, `titleInactiveColor` — color props
+ - `type`: `'line'` | `'card'`
+ - `swipeable` — enable swipe gesture switching
+ - `animated` — enable tab switch animation
+ - `ellipsis` — text overflow ellipsis (default true)
+ - **No** `lineWidth` or `lineHeight` props
+ - `onChange`: `(e: { detail: { index: number; name?: string; title?: string } }) => void`
+- `Tab`: `title` (ReactNode), `name` (identifier), `dot`, `info`, `disabled`, `titleStyle`
+
+### Image (VantUI Image, not Taro's native Image)
+- `src`, `fit` (`'contain' | 'cover' | 'fill' | 'widthFix' | 'heightFix' | 'none'`), `width`, `height`, `radius`
+- `round` — circular image (boolean)
+- `lazyLoad`, `showError`, `showLoading`
+- `renderLoading`, `renderError` — custom render slots
+
+### Common VantUI pattern: Taro's native components vs VantUI components
+- Use `@tarojs/components` for: `View`, `Text`, `ScrollView`, `Image` (native, simpler)
+- Use `@antmjs/vantui` for: styled UI components (Search, Swiper, Tabs, Grid, Button, etc.)
+- VantUI component props extend Taro's `ViewProps`, so `onClick`/`onTap` and style props are always available
diff --git a/src/app.less b/src/app.less
index 70defe9..fedbf94 100644
--- a/src/app.less
+++ b/src/app.less
@@ -1,7 +1,8 @@
+@import '@antmjs/vantui/es/style/var.less';
@import '@antmjs/vantui/lib/index.less';
page {
- background: @pageBack;
+ background: @page-back;
font-size: 28px;
font-family: @base-font-family;
color: @black;
diff --git a/src/components/ArticleCard/ImageGridCard/index.less b/src/components/ArticleCard/ImageGridCard/index.less
new file mode 100644
index 0000000..b8c6f92
--- /dev/null
+++ b/src/components/ArticleCard/ImageGridCard/index.less
@@ -0,0 +1,47 @@
+.image-grid-card {
+ .card-title {
+ font-size: 32px;
+ font-weight: 500;
+ color: #1a1a1a;
+ line-height: 1.4;
+ margin-bottom: 16px;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+ }
+
+ .card-images {
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ margin-bottom: 16px;
+
+ .grid-image {
+ flex: 1;
+ height: 160px;
+ border-radius: 8px;
+ overflow: hidden;
+ }
+ }
+
+ .card-meta {
+ display: flex;
+ align-items: center;
+ font-size: 24px;
+ color: #969799;
+
+ .meta-source {
+ margin-right: 16px;
+ }
+
+ .meta-time {
+ margin-right: 16px;
+ }
+
+ .meta-read {
+ flex: 1;
+ text-align: right;
+ }
+ }
+}
diff --git a/src/components/ArticleCard/ImageGridCard/index.tsx b/src/components/ArticleCard/ImageGridCard/index.tsx
new file mode 100644
index 0000000..79180a7
--- /dev/null
+++ b/src/components/ArticleCard/ImageGridCard/index.tsx
@@ -0,0 +1,32 @@
+import type { Article } from '@/types/article'
+import { View, Text, Image } from '@tarojs/components'
+import './index.less'
+
+interface ImageGridCardProps {
+ article: Article
+}
+
+export default function ImageGridCard({ article }: ImageGridCardProps) {
+ const images = article.images.slice(0, 3)
+
+ return (
+
+ {article.title}
+
+ {images.map((img, index) => (
+
+ ))}
+
+
+ {article.source}
+ {article.time}
+ {article.readCount}阅读
+
+
+ )
+}
diff --git a/src/components/ArticleCard/ImageTextCard/index.less b/src/components/ArticleCard/ImageTextCard/index.less
new file mode 100644
index 0000000..de4da7d
--- /dev/null
+++ b/src/components/ArticleCard/ImageTextCard/index.less
@@ -0,0 +1,63 @@
+.image-text-card {
+ display: flex;
+ flex-direction: row;
+
+ .card-thumb {
+ width: 200px;
+ height: 200px;
+ border-radius: 8px;
+ flex-shrink: 0;
+ margin-right: 20px;
+ }
+
+ .card-body {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ }
+
+ .card-title {
+ font-size: 32px;
+ font-weight: 500;
+ color: #1a1a1a;
+ line-height: 1.4;
+ margin-bottom: 8px;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+ }
+
+ .card-excerpt {
+ font-size: 26px;
+ color: #646566;
+ line-height: 1.5;
+ flex: 1;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+ }
+
+ .card-meta {
+ display: flex;
+ align-items: center;
+ font-size: 24px;
+ color: #969799;
+ margin-top: 12px;
+
+ .meta-source {
+ margin-right: 16px;
+ }
+
+ .meta-time {
+ margin-right: 16px;
+ }
+
+ .meta-read {
+ flex: 1;
+ text-align: right;
+ }
+ }
+}
diff --git a/src/components/ArticleCard/ImageTextCard/index.tsx b/src/components/ArticleCard/ImageTextCard/index.tsx
new file mode 100644
index 0000000..b3ff249
--- /dev/null
+++ b/src/components/ArticleCard/ImageTextCard/index.tsx
@@ -0,0 +1,28 @@
+import type { Article } from '@/types/article'
+import { View, Text, Image } from '@tarojs/components'
+import './index.less'
+
+interface ImageTextCardProps {
+ article: Article
+}
+
+export default function ImageTextCard({ article }: ImageTextCardProps) {
+ return (
+
+
+
+ {article.title}
+ {article.excerpt}
+
+ {article.source}
+ {article.time}
+ {article.readCount}阅读
+
+
+
+ )
+}
diff --git a/src/components/ArticleCard/TextCard/index.less b/src/components/ArticleCard/TextCard/index.less
new file mode 100644
index 0000000..eb379a6
--- /dev/null
+++ b/src/components/ArticleCard/TextCard/index.less
@@ -0,0 +1,44 @@
+.text-card {
+ .card-title {
+ font-size: 32px;
+ font-weight: 500;
+ color: #1a1a1a;
+ line-height: 1.4;
+ margin-bottom: 12px;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+ }
+
+ .card-excerpt {
+ font-size: 28px;
+ color: #646566;
+ line-height: 1.6;
+ margin-bottom: 16px;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+ }
+
+ .card-meta {
+ display: flex;
+ align-items: center;
+ font-size: 24px;
+ color: #969799;
+
+ .meta-source {
+ margin-right: 16px;
+ }
+
+ .meta-time {
+ margin-right: 16px;
+ }
+
+ .meta-read {
+ flex: 1;
+ text-align: right;
+ }
+ }
+}
diff --git a/src/components/ArticleCard/TextCard/index.tsx b/src/components/ArticleCard/TextCard/index.tsx
new file mode 100644
index 0000000..1a1975d
--- /dev/null
+++ b/src/components/ArticleCard/TextCard/index.tsx
@@ -0,0 +1,21 @@
+import type { Article } from '@/types/article'
+import { View, Text } from '@tarojs/components'
+import './index.less'
+
+interface TextCardProps {
+ article: Article
+}
+
+export default function TextCard({ article }: TextCardProps) {
+ return (
+
+ {article.title}
+ {article.excerpt}
+
+ {article.source}
+ {article.time}
+ {article.readCount}阅读
+
+
+ )
+}
diff --git a/src/components/ArticleCard/index.tsx b/src/components/ArticleCard/index.tsx
new file mode 100644
index 0000000..e6f3c8d
--- /dev/null
+++ b/src/components/ArticleCard/index.tsx
@@ -0,0 +1,23 @@
+import type { Article } from '@/types/article'
+import TextCard from './TextCard'
+import ImageTextCard from './ImageTextCard'
+import ImageGridCard from './ImageGridCard'
+
+interface ArticleCardProps {
+ article: Article
+}
+
+/** 根据文章 type 自动选择对应卡片组件 */
+export default function ArticleCard({ article }: ArticleCardProps) {
+ switch (article.type) {
+ case 'image-text':
+ return
+ case 'image-grid':
+ return
+ case 'text':
+ default:
+ return
+ }
+}
+
+export { TextCard, ImageTextCard, ImageGridCard }
diff --git a/src/mock/articles.ts b/src/mock/articles.ts
new file mode 100644
index 0000000..47979c5
--- /dev/null
+++ b/src/mock/articles.ts
@@ -0,0 +1,168 @@
+import type { Article, BannerItem, NavItem } from '@/types/article'
+
+/** 轮播图数据 */
+export const banners: BannerItem[] = [
+ {
+ id: '1',
+ image: 'https://picsum.photos/seed/banner1/750/350',
+ title: '校园新学期开学通知',
+ link: '',
+ },
+ {
+ id: '2',
+ image: 'https://picsum.photos/seed/banner2/750/350',
+ title: '2024年度优秀学生表彰',
+ link: '',
+ },
+ {
+ id: '3',
+ image: 'https://picsum.photos/seed/banner3/750/350',
+ title: '校园文化艺术节开幕',
+ link: '',
+ },
+]
+
+/** 导航组数据 (2行 x 5列 = 10项) */
+export const navItems: NavItem[] = [
+ { id: '1', label: '课程表', icon: 'orders-o', path: '' },
+ { id: '2', label: '通知公告', icon: 'bullhorn-o', path: '' },
+ { id: '3', label: '成绩查询', icon: 'chart-trending-o', path: '' },
+ { id: '4', label: '作业提交', icon: 'edit', path: '' },
+ { id: '5', label: '考试安排', icon: 'description', path: '' },
+ { id: '6', label: '校园活动', icon: 'friends-o', path: '' },
+ { id: '7', label: '图书馆', icon: 'bookmark-o', path: '' },
+ { id: '8', label: '一卡通', icon: 'credit-pay', path: '' },
+ { id: '9', label: '校园地图', icon: 'map-marked', path: '' },
+ { id: '10', label: '更多服务', icon: 'more-o', path: '' },
+]
+
+/** 文章模拟数据 */
+export const articles: Article[] = [
+ // === 最新 (latest) ===
+ {
+ id: '1',
+ title: '关于2024年秋季学期选课通知',
+ excerpt: '各位同学,2024年秋季学期选课将于8月25日上午10:00正式开始,请同学们提前查看培养方案,合理规划选课时间...',
+ source: '教务处',
+ time: '2小时前',
+ readCount: 1523,
+ images: [],
+ type: 'text',
+ category: 'latest',
+ },
+ {
+ id: '2',
+ title: '校园道路施工临时封闭公告',
+ excerpt: '因校园管网改造工程需要,东门至图书馆路段将于8月20日至9月5日进行封闭施工...',
+ source: '后勤管理处',
+ time: '3小时前',
+ readCount: 892,
+ images: ['https://picsum.photos/seed/news2/200/200'],
+ type: 'image-text',
+ category: 'latest',
+ },
+ {
+ id: '3',
+ title: '迎新志愿者招募启动,期待你的加入',
+ excerpt: '新的学期即将开始,为迎接2024级新生入学,现面向全校招募迎新志愿者...',
+ source: '学生工作部',
+ time: '5小时前',
+ readCount: 2341,
+ images: [
+ 'https://picsum.photos/seed/vol1/200/200',
+ 'https://picsum.photos/seed/vol2/200/200',
+ 'https://picsum.photos/seed/vol3/200/200',
+ ],
+ type: 'image-grid',
+ category: 'latest',
+ },
+ {
+ id: '4',
+ title: '关于调整校园班车运行时间的通知',
+ excerpt: '根据师生出行需求调研结果,自8月22日起,校园班车将调整早晚高峰时段发车频次...',
+ source: '后勤管理处',
+ time: '昨天',
+ readCount: 678,
+ images: [],
+ type: 'text',
+ category: 'latest',
+ },
+
+ // === 推荐 (recommend) ===
+ {
+ id: '5',
+ title: '优秀学长分享:大学四年如何规划学习与生活',
+ excerpt: '来自计算机学院的张同学,GPA排名年级前5%,获得多项国家级竞赛奖项,他将分享自己的大学规划经验...',
+ source: '学工在线',
+ time: '1天前',
+ readCount: 4521,
+ images: ['https://picsum.photos/seed/share1/200/200'],
+ type: 'image-text',
+ category: 'recommend',
+ },
+ {
+ id: '6',
+ title: '图书馆新书上架:2024年暑期推荐阅读书单',
+ excerpt: '图书馆最新上架图书2000余册,涵盖文学、历史、科技、艺术等多个领域,欢迎师生前来借阅...',
+ source: '图书馆',
+ time: '1天前',
+ readCount: 1892,
+ images: [
+ 'https://picsum.photos/seed/book1/200/200',
+ 'https://picsum.photos/seed/book2/200/200',
+ 'https://picsum.photos/seed/book3/200/200',
+ ],
+ type: 'image-grid',
+ category: 'recommend',
+ },
+ {
+ id: '7',
+ title: '考研经验交流会活动预告',
+ excerpt: '为帮助同学们更好地备战考研,学校将举办考研经验交流会,邀请2024届考研成功的学长学姐分享备考心得...',
+ source: '学生会',
+ time: '2天前',
+ readCount: 3156,
+ images: [],
+ type: 'text',
+ category: 'recommend',
+ },
+
+ // === 热点 (hot) ===
+ {
+ id: '8',
+ title: '我校在2024年全国大学生数学建模竞赛中再创佳绩',
+ excerpt: '近日,2024年全国大学生数学建模竞赛结果揭晓,我校参赛队伍共获得国家一等奖3项、国家二等奖7项...',
+ source: '数学学院',
+ time: '1小时前',
+ readCount: 8934,
+ images: [
+ 'https://picsum.photos/seed/award1/200/200',
+ 'https://picsum.photos/seed/award2/200/200',
+ 'https://picsum.photos/seed/award3/200/200',
+ ],
+ type: 'image-grid',
+ category: 'hot',
+ },
+ {
+ id: '9',
+ title: '关于2024年国家奖学金评选结果的公示',
+ excerpt: '根据教育部相关文件精神,经学生个人申请、学院推荐、学校评审,现将2024年国家奖学金拟获奖名单予以公示...',
+ source: '学生工作部',
+ time: '3小时前',
+ readCount: 6789,
+ images: [],
+ type: 'text',
+ category: 'hot',
+ },
+ {
+ id: '10',
+ title: '学校食堂推出新菜品,舌尖上的校园美食',
+ excerpt: '新学期伊始,学校各食堂纷纷推出新菜品,从川菜到粤菜、从面食到甜点,满足不同地域同学的口味需求...',
+ source: '后勤管理处',
+ time: '6小时前',
+ readCount: 5678,
+ images: ['https://picsum.photos/seed/food1/200/200'],
+ type: 'image-text',
+ category: 'hot',
+ },
+]
diff --git a/src/pages/index/index.config.ts b/src/pages/index/index.config.ts
index a2af952..577e9a9 100644
--- a/src/pages/index/index.config.ts
+++ b/src/pages/index/index.config.ts
@@ -1,3 +1,5 @@
export default definePageConfig({
- navigationBarTitleText: '首页'
-})
\ No newline at end of file
+ navigationBarTitleText: '首页',
+ navigationStyle: 'custom',
+ enablePullDownRefresh: false,
+})
diff --git a/src/pages/index/index.less b/src/pages/index/index.less
index e69de29..5b5d25c 100644
--- a/src/pages/index/index.less
+++ b/src/pages/index/index.less
@@ -0,0 +1,146 @@
+/* ========================================
+ 首页样式
+ ======================================== */
+
+.home-page {
+ min-height: 100vh;
+ background: #f7f8fa;
+}
+
+/* ========== 自定义导航栏 (毛玻璃) ========== */
+.custom-navbar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 999;
+ background: rgba(255, 255, 255, 0.72);
+ backdrop-filter: blur(20px);
+ -webkit-backdrop-filter: blur(20px);
+}
+
+.navbar-content {
+ display: flex;
+ align-items: center;
+ padding: 0 24px;
+
+ .navbar-search {
+ flex: 1;
+ }
+}
+
+/* ========== 滚动区域 ========== */
+.page-scroll {
+ height: 100vh;
+}
+
+/* ========== 轮播图区域 ========== */
+.swiper-section {
+ padding: 20px 24px 0;
+
+ .banner-swiper {
+ height: 320px;
+ border-radius: 12px;
+ overflow: hidden;
+ }
+
+ .swiper-card {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ border-radius: 12px;
+ overflow: hidden;
+
+ .banner-image {
+ width: 100%;
+ height: 100%;
+ }
+
+ .banner-title {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding: 24px 20px;
+ font-size: 30px;
+ font-weight: 500;
+ color: #fff;
+ background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.5) 100%);
+ border-radius: 0 0 12px 12px;
+ }
+ }
+}
+
+/* ========== 导航组 ========== */
+.nav-section {
+ margin: 24px 24px;
+ background: #fff;
+ border-radius: 16px;
+ padding: 24px 12px 12px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
+}
+
+/* ========== 标签区域 ========== */
+.tabs-section {
+ background: #fff;
+ border-radius: 16px;
+ overflow: hidden;
+ margin: 0 24px 20px;
+
+ .van-tabs__wrap {
+ border-radius: 16px 16px 0 0;
+ }
+}
+
+/* ========== 文章列表 ========== */
+.article-list {
+ padding: 0 24px;
+
+ .article-item {
+ background: #fff;
+ border-radius: 16px;
+ padding: 24px;
+ margin-bottom: 16px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
+ transition: transform 0.2s;
+
+ &:active {
+ transform: scale(0.98);
+ }
+ }
+
+ .empty-tip {
+ text-align: center;
+ padding: 80px 0;
+ font-size: 28px;
+ color: #969799;
+ }
+}
+
+/* 底部安全距离 */
+.bottom-safe {
+ height: 40px;
+}
+
+/* ========== 文章卡片通用样式 ========== */
+.article-card {
+ .card-meta {
+ display: flex;
+ align-items: center;
+ font-size: 24px;
+ color: #969799;
+
+ .meta-source {
+ margin-right: 16px;
+ }
+
+ .meta-time {
+ margin-right: 16px;
+ }
+
+ .meta-read {
+ flex: 1;
+ text-align: right;
+ }
+ }
+}
diff --git a/src/pages/index/index.tsx b/src/pages/index/index.tsx
index d7fa857..42d5f84 100644
--- a/src/pages/index/index.tsx
+++ b/src/pages/index/index.tsx
@@ -1,34 +1,168 @@
-import { View } from '@tarojs/components'
-import { Button } from '@antmjs/vantui'
+import { useState, useMemo } from 'react'
+import Taro from '@tarojs/taro'
+import { View, ScrollView } from '@tarojs/components'
+import { Search, Swiper, SwiperItem, Grid, GridItem, Tabs, Tab, Image as VantImage } from '@antmjs/vantui'
+import ArticleCard from '@/components/ArticleCard'
+import { banners, navItems, articles } from '@/mock/articles'
+import type { ArticleCategory } from '@/types/article'
import './index.less'
+/** 获取状态栏和导航栏高度 */
+function getNavBarHeight() {
+ const systemInfo = Taro.getSystemInfoSync()
+ const statusBarHeight = systemInfo.statusBarHeight || 20
+ // 微信小程序导航栏高度一般为 44px,总高度 = 状态栏 + 导航栏
+ const navBarHeight = 44
+ return {
+ statusBarHeight,
+ navBarHeight,
+ totalHeight: statusBarHeight + navBarHeight,
+ }
+}
+
+/** 分类标签映射 */
+const TAB_LIST = [
+ { title: '最新', value: 'latest' as ArticleCategory },
+ { title: '推荐', value: 'recommend' as ArticleCategory },
+ { title: '热点', value: 'hot' as ArticleCategory },
+]
+
export default function Index() {
+ const [activeTab, setActiveTab] = useState(0)
+ const navBarHeight = useMemo(() => getNavBarHeight(), [])
+
+ /** 根据当前 tab 筛选文章 */
+ const filteredArticles = useMemo(() => {
+ const category = TAB_LIST[activeTab]?.value || 'latest'
+ return articles.filter((a) => a.category === category)
+ }, [activeTab])
+
+ /** 搜索 */
+ const handleSearch = () => {
+ Taro.navigateTo({ url: '/pages/search/index' })
+ }
+
+ /** 导航项点击 */
+ const handleNavClick = (item: { path: string; label: string }) => {
+ if (item.path) {
+ Taro.navigateTo({ url: item.path })
+ } else {
+ Taro.showToast({ title: `${item.label}即将上线`, icon: 'none' })
+ }
+ }
+
+ /** 文章点击 */
+ const handleArticleClick = (id: string) => {
+ Taro.navigateTo({ url: `/pages/article/index?id=${id}` })
+ }
+
return (
-
-
- 上面的按钮的颜色已经通过全局主题重写覆盖了,参见src/styles/index.less
+
+ {/* ========== 自定义导航栏 (毛玻璃) ========== */}
+
+
+
+
+
+
+
+
+ {/* 占位,补偿固定导航栏高度 */}
+
+
+ {/* ========== 可滚动内容区 ========== */}
+
+ {/* ========== 卡片轮播图 ========== */}
+
+
+ {banners.map((banner) => (
+
+
+
+ {banner.title}
+
+
+ ))}
+
+
+
+ {/* ========== 导航组 2行 x 5列 ========== */}
+
+
+ {navItems.map((item) => (
+ handleNavClick(item)}
+ />
+ ))}
+
+
+
+ {/* ========== 文章标签导航 (最新 / 推荐 / 热点) ========== */}
+
+ setActiveTab(e.detail.index)}
+ sticky
+ offsetTop={navBarHeight.totalHeight}
+ color='#1989fa'
+ titleActiveColor='#1989fa'
+ titleInactiveColor='#646566'
+ >
+ {TAB_LIST.map((tab) => (
+
+ ))}
+
+
+
+ {/* ========== 文章列表 ========== */}
+
+ {filteredArticles.map((article) => (
+ handleArticleClick(article.id)}
+ >
+
+
+ ))}
+ {filteredArticles.length === 0 && (
+ 暂无文章
+ )}
+
+
+ {/* 底部安全距离 */}
+
+
)
}
-
-// export default class Index extends Component {
-
-// componentWillMount () { }
-
-// componentDidMount () { }
-
-// componentWillUnmount () { }
-
-// componentDidShow () { }
-
-// componentDidHide () { }
-
-// render () {
-// return (
-//
-//
-// 上面的按钮的颜色已经通过全局主题重写覆盖了,参见src/style/index.less
-//
-// )
-// }
-// }
diff --git a/src/types/article.ts b/src/types/article.ts
new file mode 100644
index 0000000..fcd3a2d
--- /dev/null
+++ b/src/types/article.ts
@@ -0,0 +1,34 @@
+/** 文章卡片类型 */
+export type ArticleCardType = 'text' | 'image-text' | 'image-grid'
+
+/** 文章分类 */
+export type ArticleCategory = 'latest' | 'recommend' | 'hot'
+
+/** 文章数据接口 */
+export interface Article {
+ id: string
+ title: string
+ excerpt: string
+ source: string
+ time: string
+ readCount: number
+ images: string[]
+ type: ArticleCardType
+ category: ArticleCategory
+}
+
+/** 导航项 */
+export interface NavItem {
+ id: string
+ label: string
+ icon: string
+ path: string
+}
+
+/** 轮播项 */
+export interface BannerItem {
+ id: string
+ image: string
+ title: string
+ link: string
+}
diff --git a/tsconfig.json b/tsconfig.json
index 0fc6d47..c5d3a09 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -14,6 +14,9 @@
"strictNullChecks": true,
"sourceMap": true,
"baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
"rootDir": ".",
"jsx": "react-jsx",
"allowJs": true,