feat: 重构项目并添加新功能

- 重命名项目为 Digital Pilates
- 更新 bundleIdentifier 为 digital-pilates
- 重新设计底部导航栏,增加选中状态和标签
- 添加 CLAUDE.md 文件,提供代码助手指导
- 删除 README.md 文件中的冗余信息
This commit is contained in:
richarjiang
2025-08-11 10:12:35 +08:00
parent 3990ce7aee
commit d3d11c9d48
4 changed files with 137 additions and 72 deletions

19
CLAUDE.md Normal file
View File

@@ -0,0 +1,19 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
- **Start development server**: `npm start`
- **Run on Android**: `npm run android`
- **Run on iOS**: `npm run ios`
- **Run on Web**: `npm run web`
- **Lint**: `npm run lint`
- **Reset project**: `npm run reset-project`
## Architecture
- **Framework**: React Native (Expo) with TypeScript.
- **Navigation**: Expo Router for file-based routing (`app/` directory).
- **UI**: Themed components (`ThemedText`, `ThemedView`) and reusable UI elements (`Collapsible`, `ParallaxScrollView`).
- **Platform-Specific**: Android (`android/`) and iOS (`ios/`) configurations with native modules.
- **Hooks**: Custom hooks for color scheme (`useColorScheme`) and theme management (`useThemeColor`).
- **Dependencies**: React Navigation for tab-based navigation, Expo modules for native features (haptics, blur, etc.).

View File

@@ -1,50 +0,0 @@
# Welcome to your Expo app 👋
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
## Get started
1. Install dependencies
```bash
npm install
```
2. Start the app
```bash
npx expo start
```
In the output, you'll find options to open the app in a
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
## Get a fresh project
When you're ready, run:
```bash
npm run reset-project
```
This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
## Learn more
To learn more about developing your project with Expo, look at the following resources:
- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
## Join the community
Join our community of developers creating universal apps.
- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.

View File

@@ -10,7 +10,7 @@
"newArchEnabled": true,
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.anonymous.digitalpilates"
"bundleIdentifier": "digital-pilates"
},
"android": {
"adaptiveIcon": {

View File

@@ -1,43 +1,139 @@
import { Tabs } from 'expo-router';
import * as Haptics from 'expo-haptics';
import { Tabs, usePathname } from 'expo-router';
import React from 'react';
import { Platform } from 'react-native';
import { Text, TouchableOpacity, View } from 'react-native';
import { HapticTab } from '@/components/HapticTab';
import { IconSymbol } from '@/components/ui/IconSymbol';
import TabBarBackground from '@/components/ui/TabBarBackground';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
export default function TabLayout() {
const colorScheme = useColorScheme();
const pathname = usePathname();
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
headerShown: false,
tabBarButton: HapticTab,
tabBarBackground: TabBarBackground,
tabBarStyle: Platform.select({
ios: {
// Use a transparent background on iOS to show the blur effect
position: 'absolute',
screenOptions={({ route }) => {
const routeName = route.name;
const isSelected = (routeName === 'index' && pathname === '/') ||
(routeName === 'explore' && pathname === '/explore') ||
pathname.includes(routeName);
return {
tabBarActiveTintColor: '#192126',
tabBarButton: (props) => {
const { children, onPress } = props;
const handlePress = (event: any) => {
if (process.env.EXPO_OS === 'ios') {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
}
onPress && onPress(event);
};
return (
<TouchableOpacity
onPress={handlePress}
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
marginHorizontal: 6,
marginVertical: 10,
borderRadius: 25,
backgroundColor: isSelected ? '#BBF246' : 'transparent',
paddingHorizontal: isSelected ? 24 : 12,
paddingVertical: 8,
minWidth: isSelected ? 140 : 48,
}}
>
{children}
</TouchableOpacity>
);
},
default: {},
}),
tabBarStyle: {
position: 'absolute',
bottom: 20,
height: 68,
borderRadius: 34,
backgroundColor: '#192126',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 10,
elevation: 5,
paddingHorizontal: 10,
paddingTop: 0,
paddingBottom: 0,
marginHorizontal: 20,
width: '90%',
alignSelf: 'center',
},
tabBarItemStyle: {
backgroundColor: 'transparent',
height: 68,
marginTop: 0,
marginBottom: 0,
paddingTop: 0,
paddingBottom: 0,
},
tabBarShowLabel: false,
};
}}>
<Tabs.Screen
name="index"
options={{
title: 'Home',
tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
tabBarIcon: ({ color }) => {
const isHomeSelected = pathname === '/' || pathname === '/index';
return (
<View style={{ flexDirection: 'column', alignItems: 'center' }}>
<IconSymbol size={22} name="house.fill" color={color} />
{isHomeSelected && (
<Text
numberOfLines={1}
style={{
color: color,
fontSize: 12,
fontWeight: '600',
marginTop: 4,
textAlign: 'center',
flexShrink: 0,
minWidth: 60,
}}>
Home
</Text>
)}
</View>
);
},
}}
/>
<Tabs.Screen
name="explore"
options={{
title: 'Explore',
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
tabBarIcon: ({ color }) => {
const isExploreSelected = pathname === '/explore';
return (
<View style={{ flexDirection: 'column', alignItems: 'center' }}>
<IconSymbol size={22} name="paperplane.fill" color={color} />
{isExploreSelected && (
<Text
numberOfLines={1}
style={{
color: color,
fontSize: 12,
fontWeight: '600',
marginTop: 4,
textAlign: 'center',
flexShrink: 0,
minWidth: 60,
}}>
Explore
</Text>
)}
</View>
);
},
}}
/>
</Tabs>