|
|
/** |
|
|
* 完整示例页面 |
|
|
* 展示所有工具的使用方法 |
|
|
*/ |
|
|
|
|
|
import React, { useState, useEffect } from 'react'; |
|
|
import { |
|
|
StyleSheet, |
|
|
View, |
|
|
Text, |
|
|
TextInput, |
|
|
TouchableOpacity, |
|
|
ScrollView, |
|
|
Switch, |
|
|
Alert, |
|
|
ActivityIndicator, |
|
|
} from 'react-native'; |
|
|
import { useForm, Controller } from 'react-hook-form'; |
|
|
import { zodResolver } from '@hookform/resolvers/zod'; |
|
|
import { Image } from 'expo-image'; |
|
|
import { useRouter } from 'expo-router'; |
|
|
|
|
|
// ✅ 扁平化导入:从根目录的各个模块导入 |
|
|
|
|
|
// 工具函数 |
|
|
import { |
|
|
Storage, |
|
|
STORAGE_KEYS, |
|
|
SessionStorage, |
|
|
SESSION_KEYS, |
|
|
formatDate, |
|
|
formatRelativeTime, |
|
|
formatChatTime |
|
|
} from '@/utils'; |
|
|
|
|
|
// 状态管理 |
|
|
import { |
|
|
useUserStore, |
|
|
useUser, |
|
|
useIsLoggedIn, |
|
|
useSettingsStore, |
|
|
useTheme, |
|
|
useLanguage, |
|
|
useHapticsEnabled, |
|
|
useSettingsActions, |
|
|
useTenantStates, |
|
|
useTenantInfo, |
|
|
} from '@/stores'; |
|
|
|
|
|
// 验证规则 |
|
|
import { loginSchema } from '@/schemas'; |
|
|
import type { LoginFormData } from '@/schemas'; |
|
|
|
|
|
// API 服务 |
|
|
import { authService } from '@/services'; |
|
|
|
|
|
// 自定义 Hooks |
|
|
import { useDebounce, useThrottle, useHaptics } from '@/hooks'; |
|
|
|
|
|
// 主题组件 |
|
|
import { ThemeDemo } from '@/components/ThemeDemo'; |
|
|
|
|
|
export default function DemoScreen() { |
|
|
console.log('=== DemoScreen 组件已渲染 ==='); |
|
|
const haptics = useHaptics(); |
|
|
const router = useRouter(); |
|
|
|
|
|
// 状态管理示例 |
|
|
const user = useUser(); |
|
|
const isLoggedIn = useIsLoggedIn(); |
|
|
const login = useUserStore((state) => state.login); |
|
|
const logout = useUserStore((state) => state.logout); |
|
|
|
|
|
const { tenantLoad } = useTenantStates(); |
|
|
const tenantInfo = useTenantInfo(); |
|
|
|
|
|
// 设置状态 |
|
|
const theme = useTheme(); |
|
|
const language = useLanguage(); |
|
|
const hapticsEnabled = useHapticsEnabled(); |
|
|
const { setTheme, setLanguage, setHapticsEnabled } = useSettingsActions(); |
|
|
// const setTheme = useSettingsStore((state) => state.setTheme); |
|
|
// const setLanguage = useSettingsStore((state) => state.setLanguage); |
|
|
// const setHapticsEnabled = useSettingsStore((state) => state.setHapticsEnabled); |
|
|
|
|
|
// 本地状态 |
|
|
const [searchText, setSearchText] = useState(''); |
|
|
const [searchResults, setSearchResults] = useState<string[]>([]); |
|
|
const [loading, setLoading] = useState(false); |
|
|
const [counter, setCounter] = useState(0); |
|
|
const [storageValue, setStorageValue] = useState(''); |
|
|
const [sessionValue, setSessionValue] = useState(''); |
|
|
|
|
|
// 表单配置 |
|
|
const { |
|
|
control, |
|
|
handleSubmit, |
|
|
formState: { errors }, |
|
|
} = useForm<LoginFormData>({ |
|
|
resolver: zodResolver(loginSchema), |
|
|
defaultValues: { |
|
|
email: '', |
|
|
password: '', |
|
|
}, |
|
|
}); |
|
|
|
|
|
// 防抖搜索示例 |
|
|
const debouncedSearch = useDebounce(async (text: string) => { |
|
|
console.log('防抖搜索:', text); |
|
|
if (!text.trim()) { |
|
|
setSearchResults([]); |
|
|
return; |
|
|
} |
|
|
|
|
|
console.log('执行搜索:', text); |
|
|
// 模拟 API 调用 |
|
|
await new Promise((resolve) => setTimeout(resolve, 500)); |
|
|
setSearchResults([`结果 1: ${text}`, `结果 2: ${text}`, `结果 3: ${text}`]); |
|
|
}, 500); |
|
|
|
|
|
// 监听搜索文本变化 |
|
|
useEffect(() => { |
|
|
debouncedSearch(searchText); |
|
|
}, [searchText]); |
|
|
|
|
|
// 节流点击示例 |
|
|
const throttledClick = useThrottle(() => { |
|
|
haptics.light(); |
|
|
setCounter((prev) => prev + 1); |
|
|
console.log('节流点击:', counter + 1); |
|
|
}, 1000); |
|
|
|
|
|
// 登录处理 |
|
|
const onLogin = async (data: LoginFormData) => { |
|
|
try { |
|
|
setLoading(true); |
|
|
haptics.light(); |
|
|
|
|
|
// 模拟登录 API 调用 |
|
|
console.log('登录数据:', data); |
|
|
|
|
|
// 实际项目中使用: |
|
|
// const { user, token } = await authService.login(data); |
|
|
// login(user, token); |
|
|
|
|
|
// 模拟登录成功 |
|
|
const mockUser = { |
|
|
id: '1', |
|
|
username: data.email.split('@')[0], |
|
|
email: data.email, |
|
|
avatar: 'https://i.pravatar.cc/150?img=1', |
|
|
nickname: '演示用户', |
|
|
createdAt: new Date().toISOString(), |
|
|
}; |
|
|
|
|
|
login(mockUser, 'mock-token-123456'); |
|
|
haptics.success(); |
|
|
Alert.alert('成功', '登录成功!'); |
|
|
} catch (error: any) { |
|
|
haptics.error(); |
|
|
Alert.alert('失败', error.message || '登录失败'); |
|
|
} finally { |
|
|
setLoading(false); |
|
|
} |
|
|
}; |
|
|
|
|
|
// 登出处理 |
|
|
const handleLogout = () => { |
|
|
haptics.warning(); |
|
|
Alert.alert('确认', '确定要退出登录吗?', [ |
|
|
{ text: '取消', style: 'cancel' }, |
|
|
{ |
|
|
text: '确定', |
|
|
onPress: () => { |
|
|
logout(); |
|
|
haptics.success(); |
|
|
}, |
|
|
}, |
|
|
]); |
|
|
}; |
|
|
|
|
|
// 存储示例 |
|
|
const handleSaveToStorage = async () => { |
|
|
try { |
|
|
haptics.light(); |
|
|
const testData = { |
|
|
message: 'Hello from AsyncStorage!', |
|
|
timestamp: new Date().toISOString(), |
|
|
counter, |
|
|
}; |
|
|
|
|
|
await Storage.setObject(STORAGE_KEYS.USER_PREFERENCES, testData); |
|
|
haptics.success(); |
|
|
Alert.alert('成功', '数据已保存到本地存储'); |
|
|
} catch (error) { |
|
|
haptics.error(); |
|
|
Alert.alert('失败', '保存失败'); |
|
|
} |
|
|
}; |
|
|
|
|
|
const handleLoadFromStorage = async () => { |
|
|
try { |
|
|
haptics.light(); |
|
|
const data = await Storage.getObject<any>(STORAGE_KEYS.USER_PREFERENCES); |
|
|
|
|
|
if (data) { |
|
|
setStorageValue(JSON.stringify(data, null, 2)); |
|
|
haptics.success(); |
|
|
} else { |
|
|
setStorageValue('暂无数据'); |
|
|
haptics.warning(); |
|
|
} |
|
|
} catch (error) { |
|
|
haptics.error(); |
|
|
Alert.alert('失败', '读取失败'); |
|
|
} |
|
|
}; |
|
|
|
|
|
// SessionStorage 处理函数 |
|
|
const handleSaveToSession = () => { |
|
|
try { |
|
|
haptics.light(); |
|
|
const testData = { |
|
|
formDraft: { |
|
|
title: '草稿标题', |
|
|
content: '这是一个表单草稿示例', |
|
|
}, |
|
|
timestamp: new Date().toISOString(), |
|
|
counter: Math.floor(Math.random() * 100), |
|
|
}; |
|
|
|
|
|
SessionStorage.setObject(SESSION_KEYS.FORM_DRAFT, testData); |
|
|
haptics.success(); |
|
|
Alert.alert('成功', '数据已保存到会话存储(应用重启后会丢失)'); |
|
|
} catch (error) { |
|
|
haptics.error(); |
|
|
Alert.alert('失败', '保存失败'); |
|
|
} |
|
|
}; |
|
|
|
|
|
const handleLoadFromSession = () => { |
|
|
try { |
|
|
haptics.light(); |
|
|
const data = SessionStorage.getObject<any>(SESSION_KEYS.FORM_DRAFT); |
|
|
|
|
|
if (data) { |
|
|
setSessionValue(JSON.stringify(data, null, 2)); |
|
|
haptics.success(); |
|
|
} else { |
|
|
setSessionValue('暂无数据(会话存储为空)'); |
|
|
haptics.warning(); |
|
|
} |
|
|
} catch (error) { |
|
|
haptics.error(); |
|
|
Alert.alert('失败', '读取失败'); |
|
|
} |
|
|
}; |
|
|
|
|
|
const handleClearSession = () => { |
|
|
try { |
|
|
haptics.light(); |
|
|
SessionStorage.clear(); |
|
|
setSessionValue(''); |
|
|
haptics.success(); |
|
|
Alert.alert('成功', '会话存储已清空'); |
|
|
} catch (error) { |
|
|
haptics.error(); |
|
|
Alert.alert('失败', '清空失败'); |
|
|
} |
|
|
}; |
|
|
|
|
|
// 主题切换 |
|
|
const handleThemeChange = () => { |
|
|
haptics.selection(); |
|
|
const themes: Array<'light' | 'dark' | 'auto'> = ['light', 'dark', 'auto']; |
|
|
const currentIndex = themes.indexOf(theme); |
|
|
const nextTheme = themes[(currentIndex + 1) % themes.length]; |
|
|
setTheme(nextTheme); |
|
|
}; |
|
|
|
|
|
// 语言切换 |
|
|
const handleLanguageChange = () => { |
|
|
haptics.selection(); |
|
|
setLanguage(language === 'zh-CN' ? 'en-US' : 'zh-CN'); |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<ScrollView style={styles.container}> |
|
|
<View style={styles.content}> |
|
|
{/* 标题 */} |
|
|
<Text style={styles.title}>🎯 完整功能演示</Text> |
|
|
<Text style={styles.subtitle}>展示所有工具的使用方法</Text> |
|
|
|
|
|
{/* 页面导航 */} |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>📱 页面导航</Text> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.primaryButton]} |
|
|
onPress={() => { |
|
|
haptics.light(); |
|
|
router.push('/test'); |
|
|
}} |
|
|
> |
|
|
<Text style={styles.buttonText}>跳转到测试页面 →</Text> |
|
|
</TouchableOpacity> |
|
|
<Text style={styles.infoText}> |
|
|
测试页面是一个独立的业务页面示例,不包含底部 tabs |
|
|
</Text> |
|
|
|
|
|
<TouchableOpacity |
|
|
style={[styles.button, { backgroundColor: '#9333ea', marginTop: 12 }]} |
|
|
onPress={() => { |
|
|
haptics.light(); |
|
|
router.push('/theme-test'); |
|
|
}} |
|
|
> |
|
|
<Text style={styles.buttonText}>🎨 主题测试页面 →</Text> |
|
|
</TouchableOpacity> |
|
|
<Text style={styles.infoText}> |
|
|
专门的主题测试页面,可以清楚地看到主题切换效果 |
|
|
</Text> |
|
|
|
|
|
<TouchableOpacity |
|
|
style={[styles.button, { backgroundColor: '#06b6d4', marginTop: 12 }]} |
|
|
onPress={() => { |
|
|
haptics.light(); |
|
|
router.push('/theme-example'); |
|
|
}} |
|
|
> |
|
|
<Text style={styles.buttonText}>📚 主题系统示例 →</Text> |
|
|
</TouchableOpacity> |
|
|
<Text style={styles.infoText}> |
|
|
展示四种主题样式使用方式(类似 CSS 类名) |
|
|
</Text> |
|
|
</View> |
|
|
|
|
|
{/* 租户信息显示 */} |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>🏢 租户信息</Text> |
|
|
<Text style={styles.infoText}> |
|
|
状态: {tenantLoad ? '✅ 已加载' : '❌ 未加载'} |
|
|
</Text> |
|
|
{tenantInfo ? ( |
|
|
<> |
|
|
<Text style={styles.infoText}>TID: {tenantInfo.tid || '无'}</Text> |
|
|
<Text style={styles.infoText}> |
|
|
创建时间: {tenantInfo.create_time || '无'} |
|
|
</Text> |
|
|
<Text style={styles.infoText}> |
|
|
域名: {tenantInfo.domain_addr || '无'} |
|
|
</Text> |
|
|
</> |
|
|
) : null} |
|
|
</View> |
|
|
|
|
|
{/* 用户状态显示 */} |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>👤 用户状态 (Zustand)</Text> |
|
|
{isLoggedIn ? ( |
|
|
<View style={styles.userInfo}> |
|
|
{user?.avatar ? ( |
|
|
<Image source={{ uri: user.avatar }} style={styles.avatar} contentFit="cover" /> |
|
|
) : null} |
|
|
<View style={styles.userDetails}> |
|
|
<Text style={styles.userName}>{user?.nickname}</Text> |
|
|
<Text style={styles.userEmail}>{user?.email}</Text> |
|
|
<Text style={styles.userDate}> |
|
|
注册时间: {formatRelativeTime(user?.createdAt || '')} |
|
|
</Text> |
|
|
</View> |
|
|
<TouchableOpacity style={[styles.button, styles.logoutButton]} onPress={handleLogout}> |
|
|
<Text style={styles.buttonText}>退出</Text> |
|
|
</TouchableOpacity> |
|
|
</View> |
|
|
) : ( |
|
|
<Text style={styles.infoText}>未登录</Text> |
|
|
)} |
|
|
</View> |
|
|
|
|
|
{/* 登录表单 */} |
|
|
{!isLoggedIn ? ( |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>🔐 登录表单 (React Hook Form + Zod)</Text> |
|
|
|
|
|
<Controller |
|
|
control={control} |
|
|
name="email" |
|
|
render={({ field: { onChange, value } }) => ( |
|
|
<View style={styles.inputGroup}> |
|
|
<Text style={styles.label}>邮箱</Text> |
|
|
<TextInput |
|
|
value={value} |
|
|
onChangeText={onChange} |
|
|
placeholder="请输入邮箱" |
|
|
keyboardType="email-address" |
|
|
autoCapitalize="none" |
|
|
style={[styles.input, errors.email && styles.inputError]} |
|
|
/> |
|
|
{errors.email && <Text style={styles.errorText}>{errors.email.message}</Text>} |
|
|
</View> |
|
|
)} |
|
|
/> |
|
|
|
|
|
<Controller |
|
|
control={control} |
|
|
name="password" |
|
|
render={({ field: { onChange, value } }) => ( |
|
|
<View style={styles.inputGroup}> |
|
|
<Text style={styles.label}>密码</Text> |
|
|
<TextInput |
|
|
value={value} |
|
|
onChangeText={onChange} |
|
|
placeholder="请输入密码" |
|
|
secureTextEntry |
|
|
style={[styles.input, errors.password && styles.inputError]} |
|
|
/> |
|
|
{errors.password ? ( |
|
|
<Text style={styles.errorText}>{errors.password.message}</Text> |
|
|
) : null} |
|
|
</View> |
|
|
)} |
|
|
/> |
|
|
|
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.loginButton]} |
|
|
onPress={handleSubmit(onLogin)} |
|
|
disabled={loading} |
|
|
> |
|
|
{loading ? ( |
|
|
<ActivityIndicator color="#fff" /> |
|
|
) : ( |
|
|
<Text style={styles.buttonText}>登录</Text> |
|
|
)} |
|
|
</TouchableOpacity> |
|
|
</View> |
|
|
) : null} |
|
|
|
|
|
{/* 搜索示例 */} |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>🔍 防抖搜索 (useDebounce)</Text> |
|
|
<TextInput |
|
|
value={searchText} |
|
|
onChangeText={setSearchText} |
|
|
placeholder="输入搜索内容..." |
|
|
style={styles.input} |
|
|
/> |
|
|
{searchResults.length > 0 ? ( |
|
|
<View style={styles.searchResults}> |
|
|
{searchResults.map((result, index) => ( |
|
|
<Text key={index} style={styles.searchResult}> |
|
|
{result} |
|
|
</Text> |
|
|
))} |
|
|
</View> |
|
|
) : null} |
|
|
</View> |
|
|
|
|
|
{/* 节流点击示例 */} |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>⏱️ 节流点击 (useThrottle)</Text> |
|
|
<Text style={styles.infoText}>点击次数: {counter}</Text> |
|
|
<TouchableOpacity style={[styles.button, styles.primaryButton]} onPress={throttledClick}> |
|
|
<Text style={styles.buttonText}>快速点击测试(1秒节流)</Text> |
|
|
</TouchableOpacity> |
|
|
</View> |
|
|
|
|
|
{/* 本地存储示例 */} |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>💾 本地存储 (AsyncStorage)</Text> |
|
|
<View style={styles.buttonRow}> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.primaryButton, styles.halfButton]} |
|
|
onPress={handleSaveToStorage} |
|
|
> |
|
|
<Text style={styles.buttonText}>保存数据</Text> |
|
|
</TouchableOpacity> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.secondaryButton, styles.halfButton]} |
|
|
onPress={handleLoadFromStorage} |
|
|
> |
|
|
<Text style={styles.buttonText}>读取数据</Text> |
|
|
</TouchableOpacity> |
|
|
</View> |
|
|
{storageValue ? ( |
|
|
<View style={styles.codeBlock}> |
|
|
<Text style={styles.codeText}>{storageValue}</Text> |
|
|
</View> |
|
|
) : null} |
|
|
</View> |
|
|
|
|
|
{/* 会话存储示例 */} |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>🔄 会话存储 (SessionStorage)</Text> |
|
|
<Text style={styles.infoText}> |
|
|
会话存储数据保存在内存中,应用重启后会丢失,适合临时数据 |
|
|
</Text> |
|
|
<View style={styles.buttonRow}> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.primaryButton, styles.thirdButton]} |
|
|
onPress={handleSaveToSession} |
|
|
> |
|
|
<Text style={styles.buttonText}>保存</Text> |
|
|
</TouchableOpacity> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.secondaryButton, styles.thirdButton]} |
|
|
onPress={handleLoadFromSession} |
|
|
> |
|
|
<Text style={styles.buttonText}>读取</Text> |
|
|
</TouchableOpacity> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.errorButton, styles.thirdButton]} |
|
|
onPress={handleClearSession} |
|
|
> |
|
|
<Text style={styles.buttonText}>清空</Text> |
|
|
</TouchableOpacity> |
|
|
</View> |
|
|
{sessionValue ? ( |
|
|
<View style={styles.codeBlock}> |
|
|
<Text style={styles.codeText}>{sessionValue}</Text> |
|
|
</View> |
|
|
) : null} |
|
|
</View> |
|
|
|
|
|
{/* 日期格式化示例 */} |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>📅 日期格式化 (Day.js)</Text> |
|
|
<Text style={styles.infoText}> |
|
|
当前时间: {formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss')} |
|
|
</Text> |
|
|
<Text style={styles.infoText}>相对时间: {formatRelativeTime(new Date())}</Text> |
|
|
<Text style={styles.infoText}>聊天时间: {formatChatTime(Date.now())}</Text> |
|
|
</View> |
|
|
|
|
|
{/* 主题演示 */} |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>🎨 主题系统演示</Text> |
|
|
<ThemeDemo /> |
|
|
</View> |
|
|
|
|
|
{/* 设置示例 */} |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>⚙️ 应用设置</Text> |
|
|
|
|
|
<View style={styles.settingRow}> |
|
|
<Text style={styles.settingLabel}>主题: {theme}</Text> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.smallButton]} |
|
|
onPress={handleThemeChange} |
|
|
> |
|
|
<Text style={styles.buttonText}>切换</Text> |
|
|
</TouchableOpacity> |
|
|
</View> |
|
|
|
|
|
<View style={styles.settingRow}> |
|
|
<Text style={styles.settingLabel}>语言: {language}</Text> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.smallButton]} |
|
|
onPress={handleLanguageChange} |
|
|
> |
|
|
<Text style={styles.buttonText}>切换</Text> |
|
|
</TouchableOpacity> |
|
|
</View> |
|
|
|
|
|
<View style={styles.settingRow}> |
|
|
<Text style={styles.settingLabel}>触觉反馈</Text> |
|
|
<Switch |
|
|
value={hapticsEnabled} |
|
|
onValueChange={(value) => { |
|
|
haptics.selection(); |
|
|
setHapticsEnabled(value); |
|
|
}} |
|
|
/> |
|
|
</View> |
|
|
</View> |
|
|
|
|
|
{/* 触觉反馈示例 */} |
|
|
<View style={styles.section}> |
|
|
<Text style={styles.sectionTitle}>📳 触觉反馈 (Expo Haptics)</Text> |
|
|
<View style={styles.buttonGrid}> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.hapticsButton]} |
|
|
onPress={() => haptics.light()} |
|
|
> |
|
|
<Text style={styles.buttonText}>Light</Text> |
|
|
</TouchableOpacity> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.hapticsButton]} |
|
|
onPress={() => haptics.medium()} |
|
|
> |
|
|
<Text style={styles.buttonText}>Medium</Text> |
|
|
</TouchableOpacity> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.hapticsButton]} |
|
|
onPress={() => haptics.heavy()} |
|
|
> |
|
|
<Text style={styles.buttonText}>Heavy</Text> |
|
|
</TouchableOpacity> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.successButton]} |
|
|
onPress={() => haptics.success()} |
|
|
> |
|
|
<Text style={styles.buttonText}>Success</Text> |
|
|
</TouchableOpacity> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.warningButton]} |
|
|
onPress={() => haptics.warning()} |
|
|
> |
|
|
<Text style={styles.buttonText}>Warning</Text> |
|
|
</TouchableOpacity> |
|
|
<TouchableOpacity |
|
|
style={[styles.button, styles.errorButton]} |
|
|
onPress={() => haptics.error()} |
|
|
> |
|
|
<Text style={styles.buttonText}>Error</Text> |
|
|
</TouchableOpacity> |
|
|
</View> |
|
|
</View> |
|
|
|
|
|
<View style={styles.footer}> |
|
|
<Text style={styles.footerText}>查看代码了解更多使用方法 📖</Text> |
|
|
</View> |
|
|
</View> |
|
|
</ScrollView> |
|
|
); |
|
|
} |
|
|
|
|
|
const styles = StyleSheet.create({ |
|
|
container: { |
|
|
flex: 1, |
|
|
backgroundColor: '#f5f5f5', |
|
|
}, |
|
|
content: { |
|
|
padding: 16, |
|
|
}, |
|
|
title: { |
|
|
fontSize: 28, |
|
|
fontWeight: 'bold', |
|
|
marginBottom: 8, |
|
|
color: '#333', |
|
|
}, |
|
|
subtitle: { |
|
|
fontSize: 16, |
|
|
color: '#666', |
|
|
marginBottom: 24, |
|
|
}, |
|
|
section: { |
|
|
backgroundColor: '#fff', |
|
|
borderRadius: 12, |
|
|
padding: 16, |
|
|
marginBottom: 16, |
|
|
shadowColor: '#000', |
|
|
shadowOffset: { width: 0, height: 2 }, |
|
|
shadowOpacity: 0.1, |
|
|
shadowRadius: 4, |
|
|
elevation: 3, |
|
|
}, |
|
|
sectionTitle: { |
|
|
fontSize: 18, |
|
|
fontWeight: '600', |
|
|
marginBottom: 12, |
|
|
color: '#333', |
|
|
}, |
|
|
userInfo: { |
|
|
flexDirection: 'row', |
|
|
alignItems: 'center', |
|
|
}, |
|
|
avatar: { |
|
|
width: 60, |
|
|
height: 60, |
|
|
borderRadius: 30, |
|
|
marginRight: 12, |
|
|
}, |
|
|
userDetails: { |
|
|
flex: 1, |
|
|
}, |
|
|
userName: { |
|
|
fontSize: 16, |
|
|
fontWeight: '600', |
|
|
color: '#333', |
|
|
}, |
|
|
userEmail: { |
|
|
fontSize: 14, |
|
|
color: '#666', |
|
|
marginTop: 2, |
|
|
}, |
|
|
userDate: { |
|
|
fontSize: 12, |
|
|
color: '#999', |
|
|
marginTop: 4, |
|
|
}, |
|
|
inputGroup: { |
|
|
marginBottom: 16, |
|
|
}, |
|
|
label: { |
|
|
fontSize: 14, |
|
|
fontWeight: '500', |
|
|
marginBottom: 6, |
|
|
color: '#333', |
|
|
}, |
|
|
input: { |
|
|
borderWidth: 1, |
|
|
borderColor: '#ddd', |
|
|
borderRadius: 8, |
|
|
padding: 12, |
|
|
fontSize: 16, |
|
|
backgroundColor: '#fff', |
|
|
}, |
|
|
inputError: { |
|
|
borderColor: '#ff3b30', |
|
|
}, |
|
|
errorText: { |
|
|
color: '#ff3b30', |
|
|
fontSize: 12, |
|
|
marginTop: 4, |
|
|
}, |
|
|
button: { |
|
|
paddingVertical: 12, |
|
|
paddingHorizontal: 20, |
|
|
borderRadius: 8, |
|
|
alignItems: 'center', |
|
|
justifyContent: 'center', |
|
|
}, |
|
|
buttonText: { |
|
|
color: '#fff', |
|
|
fontSize: 16, |
|
|
fontWeight: '600', |
|
|
}, |
|
|
loginButton: { |
|
|
backgroundColor: '#007AFF', |
|
|
marginTop: 8, |
|
|
}, |
|
|
logoutButton: { |
|
|
backgroundColor: '#ff3b30', |
|
|
paddingVertical: 8, |
|
|
paddingHorizontal: 16, |
|
|
}, |
|
|
primaryButton: { |
|
|
backgroundColor: '#007AFF', |
|
|
}, |
|
|
secondaryButton: { |
|
|
backgroundColor: '#5856D6', |
|
|
}, |
|
|
smallButton: { |
|
|
backgroundColor: '#007AFF', |
|
|
paddingVertical: 8, |
|
|
paddingHorizontal: 16, |
|
|
}, |
|
|
buttonRow: { |
|
|
flexDirection: 'row', |
|
|
justifyContent: 'space-between', |
|
|
}, |
|
|
halfButton: { |
|
|
flex: 1, |
|
|
}, |
|
|
thirdButton: { |
|
|
flex: 1, |
|
|
marginHorizontal: 4, |
|
|
}, |
|
|
searchResults: { |
|
|
marginTop: 12, |
|
|
}, |
|
|
searchResult: { |
|
|
padding: 12, |
|
|
backgroundColor: '#f9f9f9', |
|
|
borderRadius: 8, |
|
|
marginBottom: 8, |
|
|
color: '#333', |
|
|
}, |
|
|
infoText: { |
|
|
fontSize: 14, |
|
|
color: '#666', |
|
|
marginBottom: 8, |
|
|
}, |
|
|
codeBlock: { |
|
|
backgroundColor: '#f9f9f9', |
|
|
borderRadius: 8, |
|
|
padding: 12, |
|
|
marginTop: 12, |
|
|
}, |
|
|
codeText: { |
|
|
fontFamily: 'monospace', |
|
|
fontSize: 12, |
|
|
color: '#333', |
|
|
}, |
|
|
settingRow: { |
|
|
flexDirection: 'row', |
|
|
justifyContent: 'space-between', |
|
|
alignItems: 'center', |
|
|
marginBottom: 12, |
|
|
}, |
|
|
settingLabel: { |
|
|
fontSize: 16, |
|
|
color: '#333', |
|
|
}, |
|
|
buttonGrid: { |
|
|
flexDirection: 'row', |
|
|
flexWrap: 'wrap', |
|
|
marginHorizontal: -4, |
|
|
}, |
|
|
hapticsButton: { |
|
|
backgroundColor: '#5856D6', |
|
|
flex: 1, |
|
|
minWidth: '30%', |
|
|
margin: 4, |
|
|
}, |
|
|
successButton: { |
|
|
backgroundColor: '#34C759', |
|
|
flex: 1, |
|
|
margin: 4, |
|
|
minWidth: '30%', |
|
|
}, |
|
|
warningButton: { |
|
|
backgroundColor: '#FF9500', |
|
|
flex: 1, |
|
|
minWidth: '30%', |
|
|
margin: 4, |
|
|
}, |
|
|
errorButton: { |
|
|
backgroundColor: '#FF3B30', |
|
|
flex: 1, |
|
|
minWidth: '30%', |
|
|
margin: 4, |
|
|
}, |
|
|
footer: { |
|
|
marginTop: 24, |
|
|
marginBottom: 40, |
|
|
alignItems: 'center', |
|
|
}, |
|
|
footerText: { |
|
|
fontSize: 14, |
|
|
color: '#999', |
|
|
}, |
|
|
});
|
|
|
|