127 lines
4.3 KiB
TypeScript
127 lines
4.3 KiB
TypeScript
import { View, Text, Image, ScrollView, Button } from '@tarojs/components'
|
|
import Taro, { useLoad } from '@tarojs/taro'
|
|
import { useState, useEffect } from 'react'
|
|
import { getConfigs } from '../../api'
|
|
import ParticleBackground from '../../components/ParticleBackground'
|
|
import './index.scss'
|
|
|
|
export default function Index() {
|
|
const [products, setProducts] = useState<any[]>([])
|
|
const [typedText, setTypedText] = useState('')
|
|
const [loading, setLoading] = useState(true)
|
|
const [error, setError] = useState('')
|
|
const fullText = "未来已来 AI 核心驱动"
|
|
|
|
useLoad(() => {
|
|
fetchProducts()
|
|
})
|
|
|
|
useEffect(() => {
|
|
let i = 0
|
|
const interval = setInterval(() => {
|
|
i++
|
|
setTypedText(fullText.slice(0, i))
|
|
if (i >= fullText.length) clearInterval(interval)
|
|
}, 150)
|
|
return () => clearInterval(interval)
|
|
}, [])
|
|
|
|
const fetchProducts = async () => {
|
|
setLoading(true)
|
|
setError('')
|
|
try {
|
|
const res: any = await getConfigs()
|
|
const list = Array.isArray(res) ? res : (res.results || res.data || [])
|
|
setProducts(list)
|
|
} catch (err: any) {
|
|
console.error(err)
|
|
setError(err.errMsg || '加载失败,请检查网络')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const goToDetail = (id: number) => {
|
|
Taro.navigateTo({ url: `/pages/goods/detail?id=${id}` })
|
|
}
|
|
|
|
return (
|
|
<View className='page-container'>
|
|
<ParticleBackground />
|
|
|
|
<ScrollView scrollY className='content-scroll'>
|
|
<View className='scroll-inner'>
|
|
<View className='header'>
|
|
<View className='logo-box'>
|
|
<Image src='../../assets/logo.svg' className='logo-img' mode='widthFix' />
|
|
<Text className='logo-text'>QUANT SPEED</Text>
|
|
</View>
|
|
|
|
<View className='title-container'>
|
|
<Text className='title-text'>{typedText}</Text>
|
|
<Text className='cursor'>|</Text>
|
|
</View>
|
|
<Text className='subtitle'>量迹 AI 硬件为您提供最强大的边缘计算能力</Text>
|
|
</View>
|
|
|
|
{loading ? (
|
|
<View className='skeleton-wrapper'>
|
|
{[1, 2, 3].map(i => (
|
|
<View key={i} className='skeleton-card' />
|
|
))}
|
|
</View>
|
|
) : error ? (
|
|
<View className='status-box'>
|
|
<Text className='error-text'>{error}</Text>
|
|
<Button className='btn-retry' onClick={fetchProducts}>重试</Button>
|
|
</View>
|
|
) : (
|
|
<View className='product-grid'>
|
|
{products.map((item, index) => (
|
|
<View
|
|
key={item.id}
|
|
className='card fade-in-up'
|
|
style={{ animationDelay: `${index * 0.1}s` }}
|
|
onClick={() => goToDetail(item.id)}
|
|
>
|
|
<View className='card-cover'>
|
|
{item.static_image_url ? (
|
|
<Image src={item.static_image_url} mode='aspectFill' className='card-img' />
|
|
) : (
|
|
<View className='placeholder-img'>
|
|
<View className='radar-scan'></View>
|
|
</View>
|
|
)}
|
|
<View className='card-overlay' />
|
|
</View>
|
|
|
|
<View className='card-body'>
|
|
<View className='card-header'>
|
|
<Text className='card-title'>{item.name}</Text>
|
|
<Text className='price'>¥{item.price}</Text>
|
|
</View>
|
|
|
|
<Text className='card-desc'>{item.description}</Text>
|
|
|
|
<View className='tags'>
|
|
<View className='tag cyan'><Text>{item.chip_type}</Text></View>
|
|
{item.has_camera && <View className='tag blue'><Text>Camera</Text></View>}
|
|
{item.has_microphone && <View className='tag purple'><Text>Mic</Text></View>}
|
|
</View>
|
|
|
|
<View className='card-footer'>
|
|
<Button className='btn-buy'>立即购买</Button>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
))}
|
|
</View>
|
|
)}
|
|
|
|
<View className='footer-spacer' />
|
|
</View>
|
|
</ScrollView>
|
|
</View>
|
|
)
|
|
}
|