834 lines
23 KiB
TypeScript
834 lines
23 KiB
TypeScript
/**
|
||
* 完整示例页面
|
||
* 展示所有工具的使用方法
|
||
*/
|
||
|
||
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',
|
||
},
|
||
});
|