You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

834 lines
23 KiB

/**
*
* 使
*/
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';
1 month ago
import { useRouter } from 'expo-router';
1 month ago
// ✅ 扁平化导入:从根目录的各个模块导入
// 工具函数
import {
Storage,
STORAGE_KEYS,
1 month ago
SessionStorage,
SESSION_KEYS,
formatDate,
formatRelativeTime,
1 month ago
formatChatTime
} from '@/utils';
1 month ago
1 month ago
// 状态管理
import {
useUserStore,
useUser,
useIsLoggedIn,
useSettingsStore,
useTheme,
useLanguage,
useHapticsEnabled,
1 month ago
useSettingsActions,
useTenantStates,
useTenantInfo,
} from '@/stores';
// 验证规则
import { loginSchema } from '@/schemas';
import type { LoginFormData } from '@/schemas';
1 month ago
1 month ago
// API 服务
import { authService } from '@/services';
1 month ago
1 month ago
// 自定义 Hooks
import { useDebounce, useThrottle, useHaptics } from '@/hooks';
1 month ago
1 month ago
// 主题组件
import { ThemeDemo } from '@/components/ThemeDemo';
export default function DemoScreen() {
1 month ago
console.log('=== DemoScreen 组件已渲染 ===');
const haptics = useHaptics();
1 month ago
const router = useRouter();
1 month ago
// 状态管理示例
const user = useUser();
const isLoggedIn = useIsLoggedIn();
const login = useUserStore((state) => state.login);
const logout = useUserStore((state) => state.logout);
1 month ago
1 month ago
const { tenantLoad } = useTenantStates();
const tenantInfo = useTenantInfo();
// 设置状态
const theme = useTheme();
const language = useLanguage();
const hapticsEnabled = useHapticsEnabled();
1 month ago
const { setTheme, setLanguage, setHapticsEnabled } = useSettingsActions();
// const setTheme = useSettingsStore((state) => state.setTheme);
// const setLanguage = useSettingsStore((state) => state.setLanguage);
// const setHapticsEnabled = useSettingsStore((state) => state.setHapticsEnabled);
1 month ago
// 本地状态
const [searchText, setSearchText] = useState('');
const [searchResults, setSearchResults] = useState<string[]>([]);
const [loading, setLoading] = useState(false);
const [counter, setCounter] = useState(0);
const [storageValue, setStorageValue] = useState('');
1 month ago
const [sessionValue, setSessionValue] = useState('');
// 表单配置
const {
control,
handleSubmit,
formState: { errors },
} = useForm<LoginFormData>({
resolver: zodResolver(loginSchema),
defaultValues: {
email: '',
password: '',
},
});
// 防抖搜索示例
const debouncedSearch = useDebounce(async (text: string) => {
1 month ago
console.log('防抖搜索:', text);
if (!text.trim()) {
setSearchResults([]);
return;
}
console.log('执行搜索:', text);
// 模拟 API 调用
await new Promise((resolve) => setTimeout(resolve, 500));
1 month ago
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);
1 month ago
// 实际项目中使用:
// const { user, token } = await authService.login(data);
// login(user, token);
1 month ago
// 模拟登录成功
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(),
};
1 month ago
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();
1 month ago
Alert.alert('确认', '确定要退出登录吗?', [
{ text: '取消', style: 'cancel' },
{
text: '确定',
onPress: () => {
logout();
haptics.success();
},
1 month ago
},
]);
};
// 存储示例
const handleSaveToStorage = async () => {
try {
haptics.light();
const testData = {
message: 'Hello from AsyncStorage!',
timestamp: new Date().toISOString(),
counter,
};
1 month ago
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);
1 month ago
if (data) {
setStorageValue(JSON.stringify(data, null, 2));
haptics.success();
} else {
setStorageValue('暂无数据');
haptics.warning();
}
} catch (error) {
haptics.error();
Alert.alert('失败', '读取失败');
}
};
1 month ago
// 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>
1 month ago
{/* 页面导航 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>📱 </Text>
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={() => {
haptics.light();
router.push('/test-page');
}}
>
<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>
1 month ago
{tenantInfo ? (
1 month ago
<>
<Text style={styles.infoText}>TID: {tenantInfo.tid || '无'}</Text>
<Text style={styles.infoText}>
: {tenantInfo.create_time || '无'}
</Text>
<Text style={styles.infoText}>
: {tenantInfo.domain_addr || '无'}
</Text>
</>
1 month ago
) : null}
1 month ago
</View>
{/* 用户状态显示 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>👤 (Zustand)</Text>
{isLoggedIn ? (
<View style={styles.userInfo}>
1 month ago
{user?.avatar ? (
1 month ago
<Image source={{ uri: user.avatar }} style={styles.avatar} contentFit="cover" />
1 month ago
) : 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>
1 month ago
<TouchableOpacity style={[styles.button, styles.logoutButton]} onPress={handleLogout}>
<Text style={styles.buttonText}>退</Text>
</TouchableOpacity>
</View>
) : (
<Text style={styles.infoText}></Text>
)}
</View>
{/* 登录表单 */}
1 month ago
{!isLoggedIn ? (
<View style={styles.section}>
<Text style={styles.sectionTitle}>🔐 (React Hook Form + Zod)</Text>
1 month ago
<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"
1 month ago
style={[styles.input, errors.email && styles.inputError]}
/>
1 month ago
{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
1 month ago
style={[styles.input, errors.password && styles.inputError]}
/>
1 month ago
{errors.password ? (
<Text style={styles.errorText}>{errors.password.message}</Text>
1 month ago
) : null}
</View>
)}
/>
<TouchableOpacity
style={[styles.button, styles.loginButton]}
onPress={handleSubmit(onLogin)}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}></Text>
)}
</TouchableOpacity>
</View>
1 month ago
) : null}
{/* 搜索示例 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>🔍 (useDebounce)</Text>
<TextInput
value={searchText}
onChangeText={setSearchText}
placeholder="输入搜索内容..."
style={styles.input}
/>
1 month ago
{searchResults.length > 0 ? (
<View style={styles.searchResults}>
{searchResults.map((result, index) => (
<Text key={index} style={styles.searchResult}>
{result}
</Text>
))}
</View>
1 month ago
) : null}
</View>
{/* 节流点击示例 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}> (useThrottle)</Text>
<Text style={styles.infoText}>: {counter}</Text>
1 month ago
<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>
1 month ago
{storageValue ? (
<View style={styles.codeBlock}>
<Text style={styles.codeText}>{storageValue}</Text>
</View>
1 month ago
) : null}
</View>
1 month ago
{/* 会话存储示例 */}
<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>
1 month ago
{sessionValue ? (
1 month ago
<View style={styles.codeBlock}>
<Text style={styles.codeText}>{sessionValue}</Text>
</View>
1 month ago
) : null}
1 month ago
</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>
1 month ago
<Text style={styles.infoText}>: {formatRelativeTime(new Date())}</Text>
<Text style={styles.infoText}>: {formatChatTime(Date.now())}</Text>
</View>
1 month ago
{/* 主题演示 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>🎨 </Text>
<ThemeDemo />
</View>
{/* 设置示例 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}> </Text>
1 month ago
<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}>
1 month ago
<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',
1 month ago
justifyContent: 'space-between',
},
halfButton: {
flex: 1,
},
1 month ago
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',
1 month ago
marginHorizontal: -4,
},
hapticsButton: {
backgroundColor: '#5856D6',
flex: 1,
minWidth: '30%',
1 month ago
margin: 4,
},
successButton: {
backgroundColor: '#34C759',
flex: 1,
1 month ago
margin: 4,
minWidth: '30%',
},
warningButton: {
backgroundColor: '#FF9500',
flex: 1,
minWidth: '30%',
1 month ago
margin: 4,
},
errorButton: {
backgroundColor: '#FF3B30',
flex: 1,
minWidth: '30%',
1 month ago
margin: 4,
},
footer: {
marginTop: 24,
marginBottom: 40,
alignItems: 'center',
},
footerText: {
fontSize: 14,
color: '#999',
},
});