feat: update

This commit is contained in:
2025-11-05 17:24:55 +08:00
parent 61252cdf36
commit ce324c9bb5
42 changed files with 2078 additions and 448 deletions

View File

@@ -25,7 +25,8 @@ export default function TabLayout() {
// Disable the static render of the header on web
// to prevent a hydration error in React Navigation v6.
headerShown: useClientOnlyValue(false, true),
}}>
}}
>
<Tabs.Screen
name="index"
options={{

View File

@@ -27,7 +27,7 @@ import {
formatDate,
formatRelativeTime,
formatChatTime,
// 状态管理
useUserStore,
useUser,
@@ -36,14 +36,15 @@ import {
useTheme,
useLanguage,
useHapticsEnabled,
// 验证规则
loginSchema,
type LoginFormData,
// API 服务
authService,
appService,
// 自定义 Hooks
useDebounce,
useThrottle,
@@ -51,14 +52,15 @@ import {
} from '@/src';
export default function DemoScreen() {
console.log('=== DemoScreen 组件已渲染 ===');
const haptics = useHaptics();
// 状态管理示例
const user = useUser();
const isLoggedIn = useIsLoggedIn();
const login = useUserStore((state) => state.login);
const logout = useUserStore((state) => state.logout);
// 设置状态
const theme = useTheme();
const language = useLanguage();
@@ -66,7 +68,7 @@ export default function DemoScreen() {
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[]>([]);
@@ -89,6 +91,7 @@ export default function DemoScreen() {
// 防抖搜索示例
const debouncedSearch = useDebounce(async (text: string) => {
console.log('防抖搜索:', text);
if (!text.trim()) {
setSearchResults([]);
return;
@@ -97,13 +100,24 @@ export default function DemoScreen() {
console.log('执行搜索:', text);
// 模拟 API 调用
await new Promise((resolve) => setTimeout(resolve, 500));
setSearchResults([
`结果 1: ${text}`,
`结果 2: ${text}`,
`结果 3: ${text}`,
]);
setSearchResults([`结果 1: ${text}`, `结果 2: ${text}`, `结果 3: ${text}`]);
}, 500);
useEffect(() => {
console.log('=== useEffect 开始执行 ===');
console.log('appService:', appService);
console.log('getPlatformData 方法:', appService.getPlatformData);
appService
.getPlatformData()
.then((res: any) => {
console.log('getPlatformData 成功:', res);
})
.catch((err: any) => {
console.error('getPlatformData 失败:', err);
});
}, []);
// 监听搜索文本变化
useEffect(() => {
debouncedSearch(searchText);
@@ -124,11 +138,11 @@ export default function DemoScreen() {
// 模拟登录 API 调用
console.log('登录数据:', data);
// 实际项目中使用:
// const { user, token } = await authService.login(data);
// login(user, token);
// 模拟登录成功
const mockUser = {
id: '1',
@@ -138,7 +152,7 @@ export default function DemoScreen() {
nickname: '演示用户',
createdAt: new Date().toISOString(),
};
login(mockUser, 'mock-token-123456');
haptics.success();
Alert.alert('成功', '登录成功!');
@@ -153,20 +167,16 @@ export default function DemoScreen() {
// 登出处理
const handleLogout = () => {
haptics.warning();
Alert.alert(
'确认',
'确定要退出登录吗?',
[
{ text: '取消', style: 'cancel' },
{
text: '确定',
onPress: () => {
logout();
haptics.success();
},
Alert.alert('确认', '确定要退出登录吗?', [
{ text: '取消', style: 'cancel' },
{
text: '确定',
onPress: () => {
logout();
haptics.success();
},
]
);
},
]);
};
// 存储示例
@@ -178,7 +188,7 @@ export default function DemoScreen() {
timestamp: new Date().toISOString(),
counter,
};
await Storage.setObject(STORAGE_KEYS.USER_PREFERENCES, testData);
haptics.success();
Alert.alert('成功', '数据已保存到本地存储');
@@ -192,7 +202,7 @@ export default function DemoScreen() {
try {
haptics.light();
const data = await Storage.getObject<any>(STORAGE_KEYS.USER_PREFERENCES);
if (data) {
setStorageValue(JSON.stringify(data, null, 2));
haptics.success();
@@ -234,11 +244,7 @@ export default function DemoScreen() {
{isLoggedIn ? (
<View style={styles.userInfo}>
{user?.avatar && (
<Image
source={{ uri: user.avatar }}
style={styles.avatar}
contentFit="cover"
/>
<Image source={{ uri: user.avatar }} style={styles.avatar} contentFit="cover" />
)}
<View style={styles.userDetails}>
<Text style={styles.userName}>{user?.nickname}</Text>
@@ -247,10 +253,7 @@ export default function DemoScreen() {
: {formatRelativeTime(user?.createdAt || '')}
</Text>
</View>
<TouchableOpacity
style={[styles.button, styles.logoutButton]}
onPress={handleLogout}
>
<TouchableOpacity style={[styles.button, styles.logoutButton]} onPress={handleLogout}>
<Text style={styles.buttonText}>退</Text>
</TouchableOpacity>
</View>
@@ -263,7 +266,7 @@ export default function DemoScreen() {
{!isLoggedIn && (
<View style={styles.section}>
<Text style={styles.sectionTitle}>🔐 (React Hook Form + Zod)</Text>
<Controller
control={control}
name="email"
@@ -276,14 +279,9 @@ export default function DemoScreen() {
placeholder="请输入邮箱"
keyboardType="email-address"
autoCapitalize="none"
style={[
styles.input,
errors.email && styles.inputError,
]}
style={[styles.input, errors.email && styles.inputError]}
/>
{errors.email && (
<Text style={styles.errorText}>{errors.email.message}</Text>
)}
{errors.email && <Text style={styles.errorText}>{errors.email.message}</Text>}
</View>
)}
/>
@@ -299,10 +297,7 @@ export default function DemoScreen() {
onChangeText={onChange}
placeholder="请输入密码"
secureTextEntry
style={[
styles.input,
errors.password && styles.inputError,
]}
style={[styles.input, errors.password && styles.inputError]}
/>
{errors.password && (
<Text style={styles.errorText}>{errors.password.message}</Text>
@@ -349,10 +344,7 @@ export default function DemoScreen() {
<View style={styles.section}>
<Text style={styles.sectionTitle}> (useThrottle)</Text>
<Text style={styles.infoText}>: {counter}</Text>
<TouchableOpacity
style={[styles.button, styles.primaryButton]}
onPress={throttledClick}
>
<TouchableOpacity style={[styles.button, styles.primaryButton]} onPress={throttledClick}>
<Text style={styles.buttonText}>1</Text>
</TouchableOpacity>
</View>
@@ -387,18 +379,14 @@ export default function DemoScreen() {
<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>
<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>
<View style={styles.settingRow}>
<Text style={styles.settingLabel}>: {theme}</Text>
<TouchableOpacity
@@ -475,9 +463,7 @@ export default function DemoScreen() {
</View>
<View style={styles.footer}>
<Text style={styles.footerText}>
使 📖
</Text>
<Text style={styles.footerText}>使 📖</Text>
</View>
</View>
</ScrollView>
@@ -607,7 +593,7 @@ const styles = StyleSheet.create({
},
buttonRow: {
flexDirection: 'row',
gap: 12,
justifyContent: 'space-between',
},
halfButton: {
flex: 1,
@@ -651,27 +637,31 @@ const styles = StyleSheet.create({
buttonGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8,
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,
@@ -683,4 +673,3 @@ const styles = StyleSheet.create({
color: '#999',
},
});

View File

@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { StyleSheet, TouchableOpacity, Alert, ActivityIndicator } from 'react-native';
import * as Updates from 'expo-updates';
@@ -24,23 +24,19 @@ export default function TabOneScreen() {
setUpdateInfo('发现新版本,正在下载...');
await Updates.fetchUpdateAsync();
Alert.alert(
'更新完成',
'新版本已下载完成,是否立即重启应用?',
[
{
text: '稍后',
style: 'cancel',
onPress: () => setUpdateInfo('更新已下载,稍后重启应用即可应用'),
Alert.alert('更新完成', '新版本已下载完成,是否立即重启应用?', [
{
text: '稍后',
style: 'cancel',
onPress: () => setUpdateInfo('更新已下载,稍后重启应用即可应用'),
},
{
text: '立即重启',
onPress: async () => {
await Updates.reloadAsync();
},
{
text: '立即重启',
onPress: async () => {
await Updates.reloadAsync();
},
},
]
);
},
]);
} else {
setUpdateInfo('当前已是最新版本');
}
@@ -53,13 +49,8 @@ export default function TabOneScreen() {
};
const getUpdateInfo = () => {
const {
isEmbeddedLaunch,
isEmergencyLaunch,
updateId,
channel,
runtimeVersion,
} = Updates.useUpdates();
const { isEmbeddedLaunch, isEmergencyLaunch, updateId, channel, runtimeVersion } =
Updates.useUpdates();
return `
运行模式: ${__DEV__ ? '开发模式' : '生产模式'}
@@ -71,6 +62,10 @@ export default function TabOneScreen() {
`.trim();
};
useEffect(() => {
console.log('=== TabOneScreen 组件已渲染 ===');
}, []);
return (
<View style={styles.container}>
<Text style={styles.title}>🚀 </Text>

View File

@@ -82,7 +82,7 @@ export default function PaperDemo() {
🔘
</Text>
<View style={styles.buttonRow}>
<Button mode="contained" onPress={() => setSnackbarVisible(true)}>
<Button mode="contained" onPress={() => setSnackbarVisible(true)} style={{ marginRight: 8 }}>
Contained
</Button>
<Button mode="outlined" onPress={() => setSnackbarVisible(true)}>
@@ -90,14 +90,10 @@ export default function PaperDemo() {
</Button>
</View>
<View style={styles.buttonRow}>
<Button mode="text" onPress={() => setSnackbarVisible(true)}>
<Button mode="text" onPress={() => setSnackbarVisible(true)} style={{ marginRight: 8 }}>
Text
</Button>
<Button
mode="elevated"
onPress={() => setSnackbarVisible(true)}
icon="camera"
>
<Button mode="elevated" onPress={() => setSnackbarVisible(true)} icon="camera">
With Icon
</Button>
</View>
@@ -141,18 +137,13 @@ export default function PaperDemo() {
<Switch value={switchValue} onValueChange={setSwitchValue} />
</View>
<View style={styles.chipContainer}>
<Chip icon="star" onPress={() => {}}>
<Chip icon="star" onPress={() => {}} style={{ marginRight: 8, marginBottom: 8 }}>
</Chip>
<Chip icon="heart" mode="outlined" onPress={() => {}}>
<Chip icon="heart" mode="outlined" onPress={() => {}} style={{ marginRight: 8, marginBottom: 8 }}>
</Chip>
<Chip
icon="close"
onPress={() => {}}
onClose={() => {}}
closeIcon="close-circle"
>
<Chip icon="close" onPress={() => {}} onClose={() => {}} closeIcon="close-circle" style={{ marginBottom: 8 }}>
</Chip>
</View>
@@ -221,12 +212,7 @@ export default function PaperDemo() {
</ScrollView>
{/* 浮动操作按钮 */}
<FAB
icon="plus"
style={styles.fab}
onPress={() => setSnackbarVisible(true)}
label="添加"
/>
<FAB icon="plus" style={styles.fab} onPress={() => setSnackbarVisible(true)} label="添加" />
{/* 提示条 */}
<Snackbar
@@ -279,7 +265,6 @@ const styles = StyleSheet.create({
},
buttonRow: {
flexDirection: 'row',
gap: 8,
marginBottom: 8,
},
row: {
@@ -291,7 +276,6 @@ const styles = StyleSheet.create({
chipContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8,
},
progressBar: {
marginBottom: 12,
@@ -306,4 +290,3 @@ const styles = StyleSheet.create({
bottom: 0,
},
});

View File

@@ -56,22 +56,18 @@ export default function RootLayout() {
await Updates.fetchUpdateAsync();
// 提示用户重启应用以应用更新
Alert.alert(
'更新可用',
'发现新版本,是否立即重启应用?',
[
{
text: '稍后',
style: 'cancel',
Alert.alert('更新可用', '发现新版本,是否立即重启应用?', [
{
text: '稍后',
style: 'cancel',
},
{
text: '立即重启',
onPress: async () => {
await Updates.reloadAsync();
},
{
text: '立即重启',
onPress: async () => {
await Updates.reloadAsync();
},
},
]
);
},
]);
}
} catch (error) {
// 处理更新检查错误