feat: 应用界面重构 - 从模板中心到AI内容生成平台

- 重构首页:从模板列表展示改为AI功能展示界面
- 更新底部导航:将explore改为content,index改为home
- 新增多个页面:积分、兑换、历史记录、结果展示、设置等
- 优化UI组件:新增通用按钮、分类标签、加载状态等组件
- 清理冗余文件:删除旧的认证文档和积分详情页面
- 更新主题配置:调整配色方案和视觉风格
- 修复导入路径:优化组件导入结构

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
imeepos
2025-10-31 17:53:56 +08:00
parent 668caaa91f
commit b9399bf4cf
38 changed files with 5641 additions and 1663 deletions

View File

@@ -4,8 +4,8 @@ import React from 'react';
import { HapticTab } from '@/components/haptic-tab';
import { IconSymbol } from '@/components/ui/icon-symbol';
import { Colors } from '@/constants/theme';
import { useColorScheme } from '@/hooks/use-color-scheme';
import { useAuth } from '@/hooks/use-auth';
import { useColorScheme } from '@/hooks/use-color-scheme';
export default function TabLayout() {
const colorScheme = useColorScheme();
@@ -23,16 +23,16 @@ export default function TabLayout() {
tabBarButton: HapticTab,
}}>
<Tabs.Screen
name="index"
name="home"
options={{
title: 'Home',
tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
}}
/>
<Tabs.Screen
name="explore"
name="history"
options={{
title: 'Explore',
title: 'Content',
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
}}
/>

View File

@@ -1,205 +0,0 @@
import { useMemo, useState } from 'react';
import { ScrollView, StyleSheet, View, TouchableOpacity, Text } from 'react-native';
import {
CategoryTabs,
CommunityGrid,
FeatureCarousel,
Header,
PageLayout,
SectionHeader,
StatusBarSpacer,
type CommunityItem,
type FeatureItem,
} from '@/components/bestai';
import { useAuth } from '@/hooks/use-auth';
import { useAuthGuard } from '@/hooks/use-auth-guard';
const categories = [
{ id: 'visual-effects', label: 'Visual Effects' },
{ id: 'higgsfield-soul', label: 'Higgsfield Soul' },
{ id: 'higgsfield-apps', label: 'Higgsfield Apps' },
{ id: 'king', label: 'King' },
];
const baseFeatureItems: FeatureItem[] = [
{
id: 'sketch-video',
title: 'UNLIMITED SKETCH TO VIDEO',
subtitle: 'Bring your imagination to life with Sora 2',
image: 'https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=800&q=80',
},
{
id: 'portrait-video',
title: 'UNLIMITED PORTRAIT TO VIDEO',
subtitle: 'Transform portraits into motion-driven stories',
image: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=800&q=80',
},
{
id: 'water-simulation',
title: 'FLUID SIMULATION PACK',
subtitle: 'Realistic water physics for cinematic scenes',
image: 'https://images.unsplash.com/photo-1505744386214-51dba16a26fc?auto=format&fit=crop&w=800&q=80',
},
];
const baseCommunityItems: CommunityItem[] = [
{
id: 'community-1',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
{
id: 'community-2',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1544723795-3fb6469f5b39?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
{
id: 'community-3',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
{
id: 'community-4',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1531259683007-016a7b628fc4?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
];
export default function ExploreScreen() {
const { isAuthenticated } = useAuth();
const { requireAuth } = useAuthGuard();
const [activeCategory, setActiveCategory] = useState(categories[0]?.id ?? 'visual-effects');
const featureItems = useMemo(() => {
return baseFeatureItems.map((item, index) => ({
...item,
id: `${activeCategory}-${index}`,
}));
}, [activeCategory]);
const communityItems = useMemo(() => {
return baseCommunityItems.map((item, index) => ({
...item,
id: `${activeCategory}-${index}`,
}));
}, [activeCategory]);
const handleGeneratePress = () => {
requireAuth(() => {
console.log('用户已登录,执行生成操作');
});
};
if (!isAuthenticated) {
return (
<PageLayout backgroundColor="#050505">
<ScrollView
style={styles.scroll}
contentContainerStyle={styles.contentContainer}
showsVerticalScrollIndicator={false}
>
<StatusBarSpacer />
<Header />
<View style={styles.loginPrompt}>
<View style={styles.lockIconContainer}>
<Text style={styles.lockIcon}>🔒</Text>
</View>
<Text style={styles.loginTitle}></Text>
<Text style={styles.loginSubtitle}>
使
</Text>
<TouchableOpacity
style={styles.loginButton}
onPress={() => requireAuth()}
activeOpacity={0.8}
>
<Text style={styles.loginButtonText}></Text>
</TouchableOpacity>
</View>
<View style={styles.bottomSpacer} />
</ScrollView>
</PageLayout>
);
}
return (
<PageLayout backgroundColor="#050505">
<ScrollView
style={styles.scroll}
contentContainerStyle={styles.contentContainer}
showsVerticalScrollIndicator={false}
>
<StatusBarSpacer />
<Header />
<CategoryTabs categories={categories} activeId={activeCategory} onChange={setActiveCategory} />
<FeatureCarousel items={featureItems} />
<SectionHeader title="WAN 2.5 COMMUNITY" />
<CommunityGrid items={communityItems} />
<View style={styles.bottomSpacer} />
</ScrollView>
</PageLayout>
);
}
const styles = StyleSheet.create({
scroll: {
flex: 1,
},
contentContainer: {
paddingTop: 16,
paddingBottom: 48,
},
bottomSpacer: {
height: 32,
},
loginPrompt: {
alignItems: 'center',
paddingVertical: 60,
paddingHorizontal: 32,
},
lockIconContainer: {
width: 80,
height: 80,
borderRadius: 40,
backgroundColor: 'rgba(255, 255, 255, 0.1)',
alignItems: 'center',
justifyContent: 'center',
marginBottom: 24,
},
lockIcon: {
fontSize: 40,
},
loginTitle: {
fontSize: 24,
fontWeight: '700',
color: '#ffffff',
marginBottom: 12,
},
loginSubtitle: {
fontSize: 16,
color: 'rgba(255, 255, 255, 0.6)',
textAlign: 'center',
lineHeight: 24,
marginBottom: 32,
},
loginButton: {
backgroundColor: '#d7ff1f',
paddingHorizontal: 32,
paddingVertical: 16,
borderRadius: 12,
},
loginButtonText: {
color: '#050505',
fontSize: 16,
fontWeight: '700',
},
});

149
app/(tabs)/history.tsx Normal file
View File

@@ -0,0 +1,149 @@
import { ThemedView } from '@/components/themed-view';
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { Image, ScrollView, StyleSheet, Text, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
type GalleryLane = 'leading' | 'trailing';
interface GalleryTile {
id: string;
lane: GalleryLane;
uri: string;
height: number;
}
const curatedGallery: GalleryTile[] = [
{
id: 'ember-circle',
lane: 'leading',
uri: 'https://images.unsplash.com/photo-1616004655122-818af0f3efc6?auto=format&fit=crop&w=1200&q=80',
height: 232,
},
{
id: 'ocean-horizon',
lane: 'trailing',
uri: 'https://images.unsplash.com/photo-1469474968028-56623f02e42e?auto=format&fit=crop&w=1200&q=80',
height: 152,
},
{
id: 'studio-poise',
lane: 'trailing',
uri: 'https://images.unsplash.com/photo-1582096897407-9b6c86e1a8d3?auto=format&fit=crop&w=1200&q=80',
height: 232,
},
{
id: 'duo-portrait',
lane: 'leading',
uri: 'https://images.unsplash.com/photo-1601040122900-86ad461b8234?auto=format&fit=crop&w=1200&q=80',
height: 148,
},
{
id: 'sage-armor',
lane: 'leading',
uri: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=1200&q=80',
height: 224,
},
{
id: 'velvet-repose',
lane: 'trailing',
uri: 'https://images.unsplash.com/photo-1515378791036-0648a3ef77b2?auto=format&fit=crop&w=1200&q=80',
height: 188,
},
{
id: 'nocturne-lounge',
lane: 'trailing',
uri: 'https://images.unsplash.com/photo-1573497491208-6b1acb260507?auto=format&fit=crop&w=1200&q=80',
height: 184,
},
{
id: 'sunlit-duet',
lane: 'leading',
uri: 'https://images.unsplash.com/photo-1531257240576-a4a0ef4af1c8?auto=format&fit=crop&w=1200&q=80',
height: 188,
},
];
const leadingNarratives = curatedGallery.filter(tile => tile.lane === 'leading');
const trailingNarratives = curatedGallery.filter(tile => tile.lane === 'trailing');
export default function HistoryScreen() {
return (
<ThemedView style={styles.canvas} lightColor="#050505" darkColor="#050505">
<StatusBar style="light" />
<SafeAreaView style={styles.safeArea} edges={['top', 'left', 'right']}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.scrollContent}
>
<Text style={styles.heading}>Content Generation</Text>
<Text style={styles.dateline}>10.19 Wednesday</Text>
<View style={styles.gallery}>
<View style={styles.leadingLane}>
{leadingNarratives.map(tile => (
<Image
key={tile.id}
source={{ uri: tile.uri }}
style={[styles.frame, { height: tile.height }]}
/>
))}
</View>
<View style={styles.trailingLane}>
{trailingNarratives.map(tile => (
<Image
key={tile.id}
source={{ uri: tile.uri }}
style={[styles.frame, { height: tile.height }]}
/>
))}
</View>
</View>
</ScrollView>
</SafeAreaView>
</ThemedView>
);
}
const styles = StyleSheet.create({
canvas: {
flex: 1,
},
safeArea: {
flex: 1,
},
scrollContent: {
paddingHorizontal: 24,
paddingTop: 24,
paddingBottom: 48,
},
heading: {
fontSize: 24,
fontWeight: '600',
color: '#FFFFFF',
textAlign: 'center',
letterSpacing: 0.4,
},
dateline: {
marginTop: 16,
fontSize: 16,
fontWeight: '500',
color: '#EDEDED',
},
gallery: {
flexDirection: 'row',
marginTop: 24,
},
leadingLane: {
flex: 1,
paddingRight: 12,
},
trailingLane: {
flex: 1,
paddingLeft: 12,
},
frame: {
width: '100%',
borderRadius: 28,
marginBottom: 16,
},
});

View File

@@ -1,349 +1,139 @@
import { TemplateList } from '@/components/templates/template-list';
import { ThemedText } from '@/components/themed-text';
import { ThemedView } from '@/components/themed-view';
import { useMemo, useState } from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import {
CategoryTabs,
CommunityGrid,
FeatureCarousel,
Header,
PageLayout,
SectionHeader,
StatusBarSpacer,
type CommunityItem,
type FeatureItem,
} from '@/components/bestai';
import type { FeatureItem as FeatureItemType } from '@/components/bestai/feature-carousel';
import { useAuth } from '@/hooks/use-auth';
import { getTemplates } from '@/lib/api/templates';
import { Template } from '@/lib/types/template';
import { LinearGradient } from 'expo-linear-gradient';
import { Ionicons } from '@expo/vector-icons';
import { useCallback, useEffect, useRef, useState } from 'react';
import { Alert, Animated, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { LoginModal } from '@/components/auth/login-modal';
import { useAuthGuard } from '@/hooks/use-auth-guard';
export default function HomeScreen() {
const categories = [
{ id: 'visual-effects', label: 'Visual Effects' },
{ id: 'higgsfield-soul', label: 'Higgsfield Soul' },
{ id: 'higgsfield-apps', label: 'Higgsfield Apps' },
{ id: 'king', label: 'King' },
];
const baseFeatureItems: FeatureItem[] = [
{
id: 'sketch-video',
title: 'UNLIMITED SKETCH TO VIDEO',
subtitle: 'Bring your imagination to life with Sora 2',
image: 'https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=800&q=80',
},
{
id: 'portrait-video',
title: 'UNLIMITED PORTRAIT TO VIDEO',
subtitle: 'Transform portraits into motion-driven stories',
image: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=800&q=80',
},
{
id: 'water-simulation',
title: 'FLUID SIMULATION PACK',
subtitle: 'Realistic water physics for cinematic scenes',
image: 'https://images.unsplash.com/photo-1505744386214-51dba16a26fc?auto=format&fit=crop&w=800&q=80',
},
];
const baseCommunityItems: CommunityItem[] = [
{
id: 'community-1',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
{
id: 'community-2',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1544723795-3fb6469f5b39?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
{
id: 'community-3',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
{
id: 'community-4',
chip: '64/10',
title: 'OBJECTS AROUND',
image: 'https://images.unsplash.com/photo-1531259683007-016a7b628fc4?auto=format&fit=crop&w=800&q=80',
actionLabel: 'Generate',
},
];
export default function ExploreScreen() {
const { isAuthenticated } = useAuth();
const [templates, setTemplates] = useState<Template[]>([]);
const [loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [isLoadingMore, setIsLoadingMore] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const [error, setError] = useState<string | null>(null);
const [showLoginModal, setShowLoginModal] = useState(false);
const pulseAnim = useRef(new Animated.Value(0)).current;
const bounceAnim = useRef(new Animated.Value(0)).current;
const { requireAuth } = useAuthGuard();
const [activeCategory, setActiveCategory] = useState(categories[0]?.id ?? 'visual-effects');
const fetchTemplates = useCallback(async (page: number = 1, isRefreshing = false, isLoadMore = false) => {
if (!isAuthenticated) return;
const featureItems = useMemo(() => {
return baseFeatureItems.map((item, index) => ({
...item,
id: `${activeCategory}-${index}`,
}));
}, [activeCategory]);
try {
if (isRefreshing) {
setRefreshing(true);
} else if (isLoadMore) {
setIsLoadingMore(true);
} else {
setLoading(true);
}
setError(null);
const communityItems = useMemo(() => {
return baseCommunityItems.map((item, index) => ({
...item,
id: `${activeCategory}-${index}`,
}));
}, [activeCategory]);
const response = await getTemplates({
page,
size: 10,
status: 'RELEASE'
});
if (isRefreshing || page === 1) {
setTemplates(response.data);
setCurrentPage(1);
} else {
setTemplates(prev => [...prev, ...response.data]);
setCurrentPage(page);
}
setHasMore(response.pagination.page < response.pagination.totalPages);
} catch (err: any) {
console.error('获取模板列表失败:', err);
setError(err.message || '获取模板列表失败');
} finally {
setLoading(false);
setRefreshing(false);
setIsLoadingMore(false);
}
}, [isAuthenticated]);
const loadMore = useCallback(() => {
if (!isLoadingMore && hasMore && !refreshing) {
fetchTemplates(currentPage + 1, false, true);
}
}, [currentPage, hasMore, isLoadingMore, refreshing, fetchTemplates]);
useEffect(() => {
if (isAuthenticated) {
fetchTemplates();
setShowLoginModal(false);
}
Animated.loop(
Animated.sequence([
Animated.timing(pulseAnim, {
toValue: 1,
duration: 2000,
useNativeDriver: false,
}),
Animated.timing(pulseAnim, {
toValue: 0,
duration: 2000,
useNativeDriver: false,
}),
])
).start();
Animated.loop(
Animated.sequence([
Animated.timing(bounceAnim, {
toValue: -10,
duration: 600,
useNativeDriver: true,
}),
Animated.timing(bounceAnim, {
toValue: -5,
duration: 200,
useNativeDriver: true,
}),
Animated.timing(bounceAnim, {
toValue: 0,
duration: 600,
useNativeDriver: true,
}),
Animated.timing(bounceAnim, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}),
])
).start();
}, [isAuthenticated, fetchTemplates, pulseAnim, bounceAnim]);
const handleRefresh = useCallback(() => {
fetchTemplates(1, true, false);
}, [fetchTemplates]);
const handleTemplatePress = (template: Template) => {
Alert.alert('模板详情', `您选择了: ${template.title}`);
};
const handleVideoChange = (template: Template, index: number) => {
console.log('视频切换到:', template.title, '索引:', index);
};
if (!isAuthenticated) {
const scaleAnim = pulseAnim.interpolate({
inputRange: [0, 1],
outputRange: [0.95, 1.05],
const handleGeneratePress = () => {
requireAuth(() => {
console.log('用户已登录,执行生成操作');
});
};
return (
<ThemedView style={styles.container}>
<View style={styles.welcomeContainer}>
<Animated.View
style={[
styles.iconContainer,
{ transform: [{ scale: scaleAnim }] },
]}
>
<Ionicons name="albums-outline" size={80} color="#007AFF" />
</Animated.View>
<ThemedText type="title" style={styles.welcomeTitle}>
使
</ThemedText>
<ThemedText style={styles.welcomeSubtitle}>
使
</ThemedText>
<TouchableOpacity
style={styles.loginButton}
onPress={() => setShowLoginModal(true)}
activeOpacity={0.8}
>
<LinearGradient
colors={['#007AFF', '#0056D2']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.loginButtonGradient}
>
<Ionicons name="log-in-outline" size={20} color="#fff" style={styles.loginButtonIcon} />
<Text style={styles.loginButtonText}></Text>
</LinearGradient>
</TouchableOpacity>
<TouchableOpacity style={styles.guestButton} activeOpacity={0.6}>
<Text style={styles.guestButtonText}></Text>
</TouchableOpacity>
</View>
<LoginModal
visible={showLoginModal}
onClose={() => setShowLoginModal(false)}
/>
</ThemedView>
);
}
const shadowOpacity = pulseAnim.interpolate({
inputRange: [0, 1],
outputRange: [0.3, 0.4],
});
const handleFeaturePress = (item: FeatureItemType) => {
requireAuth(() => {
console.log('用户已登录,使用功能:', item.title);
});
};
return (
<ThemedView style={styles.container}>
<Animated.View style={[styles.headerBanner, { shadowOpacity }]}>
<LinearGradient
colors={['#ff6b6b', '#ff8e53', '#ff6b35']}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.gradientBanner}
>
<Text style={styles.bannerText}>
New User 50% Off Coupon : <Text style={styles.couponText}>NEWUSER</Text>
</Text>
<Animated.Text
style={[
styles.giftIcon,
{ transform: [{ translateY: bounceAnim }] },
]}
>
🎁
</Animated.Text>
</LinearGradient>
</Animated.View>
<TemplateList
templates={templates}
loading={loading}
refreshing={refreshing}
isLoadingMore={isLoadingMore}
hasMore={hasMore}
onRefresh={handleRefresh}
onLoadMore={loadMore}
onTemplatePress={handleTemplatePress}
onVideoChange={handleVideoChange}
error={error}
/>
<LoginModal
visible={showLoginModal}
onClose={() => setShowLoginModal(false)}
/>
</ThemedView>
<PageLayout backgroundColor="#050505">
<ScrollView
style={styles.scroll}
contentContainerStyle={styles.contentContainer}
showsVerticalScrollIndicator={false}
>
<StatusBarSpacer />
<Header />
<CategoryTabs categories={categories} activeId={activeCategory} onChange={setActiveCategory} />
<FeatureCarousel items={featureItems} onPress={handleFeaturePress} />
<SectionHeader title="WAN 2.5 COMMUNITY" />
<CommunityGrid items={communityItems} onPressAction={handleGeneratePress} />
<View style={styles.bottomSpacer} />
</ScrollView>
</PageLayout>
);
}
const styles = StyleSheet.create({
container: {
scroll: {
flex: 1,
},
welcomeContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 40,
contentContainer: {
paddingTop: 16,
paddingBottom: 48,
},
iconContainer: {
width: 120,
height: 120,
borderRadius: 60,
backgroundColor: 'rgba(0, 122, 255, 0.1)',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 24,
},
welcomeTitle: {
fontSize: 28,
fontWeight: '700',
marginBottom: 12,
textAlign: 'center',
},
welcomeSubtitle: {
fontSize: 16,
textAlign: 'center',
opacity: 0.7,
marginBottom: 48,
lineHeight: 24,
},
loginButton: {
width: '100%',
height: 56,
borderRadius: 16,
overflow: 'hidden',
elevation: 8,
shadowColor: '#007AFF',
shadowOffset: { width: 0, height: 4 },
shadowRadius: 12,
shadowOpacity: 0.3,
},
loginButtonGradient: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
height: '100%',
},
loginButtonIcon: {
marginRight: 8,
},
loginButtonText: {
color: '#fff',
fontSize: 17,
fontWeight: '700',
},
guestButton: {
width: '100%',
height: 56,
borderRadius: 16,
borderWidth: 1.5,
borderColor: 'rgba(0, 122, 255, 0.3)',
alignItems: 'center',
justifyContent: 'center',
marginTop: 12,
},
guestButtonText: {
color: '#007AFF',
fontSize: 16,
fontWeight: '600',
},
headerBanner: {
position: 'absolute',
top: 16,
left: '10%',
right: '10%',
zIndex: 1000,
borderRadius: 20,
shadowColor: '#ff6b6b',
shadowOffset: { width: 0, height: 4 },
shadowRadius: 12,
elevation: 8,
overflow: 'hidden',
},
gradientBanner: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 4,
paddingHorizontal: 16,
position: 'relative',
},
bannerText: {
fontSize: 14,
fontWeight: '600',
color: '#fff',
textAlign: 'center',
},
couponText: {
fontWeight: '800',
fontSize: 14,
color: '#fff',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 6,
marginLeft: 6,
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.6)',
borderStyle: 'dashed',
},
giftIcon: {
position: 'absolute',
right: 8,
fontSize: 16,
bottomSpacer: {
height: 32,
},
});

View File

@@ -48,7 +48,6 @@ const TABS: { key: TabKey; label: string }[] = [
const BILLING_OPTIONS: { key: BillingMode; label: string }[] = [
{ key: 'monthly', label: '月付' },
{ key: 'lifetime', label: '终身' },
];
const palettes: Record<'dark' | 'light', Palette> = {