Compare commits

...

5 Commits

Author SHA1 Message Date
CalebChen768
3c3b84c3bd Subscription Home Screen 2024-03-19 14:23:45 +08:00
CalebChen768
c52ec09119 Icon Problem Fixed 2024-03-19 11:56:17 +08:00
CalebChen768
800ec5ead1 test bottom navigator 2024-03-19 00:00:58 +08:00
CalebChen768
f13166f490 test database 2024-03-19 00:00:35 +08:00
CalebChen768
67a4f27b8d international languages support 2024-03-18 23:58:51 +08:00
21 changed files with 1944 additions and 1050 deletions

118
App.tsx
View File

@ -1,118 +0,0 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
*/
import React from 'react';
import type {PropsWithChildren} from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Colors,
DebugInstructions,
Header,
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
type SectionProps = PropsWithChildren<{
title: string;
}>;
function Section({children, title}: SectionProps): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
return (
<View style={styles.sectionContainer}>
<Text
style={[
styles.sectionTitle,
{
color: isDarkMode ? Colors.white : Colors.black,
},
]}>
{title}
</Text>
<Text
style={[
styles.sectionDescription,
{
color: isDarkMode ? Colors.light : Colors.dark,
},
]}>
{children}
</Text>
</View>
);
}
function App(): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
return (
<SafeAreaView style={backgroundStyle}>
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={backgroundStyle.backgroundColor}
/>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
<Header />
<View
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
<Section title="Step One">
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
screen and then come back to see your edits.
</Section>
<Section title="See Your Changes">
<ReloadInstructions />
</Section>
<Section title="Debug">
<DebugInstructions />
</Section>
<Section title="Learn More">
Read the docs to discover what to do next:
</Section>
<LearnMoreLinks />
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
},
highlight: {
fontWeight: '700',
},
});
export default App;

View File

@ -4,7 +4,7 @@
import 'react-native';
import React from 'react';
import App from '../App';
import App from '../src/App.tsx';
// Note: import explicitly to use the types shipped with jest.
import {it} from '@jest/globals';

View File

@ -3,7 +3,8 @@
*/
import {AppRegistry} from 'react-native';
import App from './App';
import App from './src/App';
import {name as appName} from './app.json';
import 'react-native-gesture-handler';
AppRegistry.registerComponent(appName, () => App);

View File

@ -10,6 +10,10 @@
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>UIAppFonts</key>
<array>
<string>Ionicons.ttf</string>
</array>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@ -26,7 +30,6 @@
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>

View File

@ -28,6 +28,8 @@ end
target 'MyAccountApp' do
config = use_native_modules!
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
use_react_native!(
:path => config[:reactNativePath],
# Enables Flipper.

View File

@ -1,5 +1,7 @@
PODS:
- boost (1.83.0)
- BVLinearGradient (2.8.3):
- React-Core
- CocoaAsyncSocket (7.6.5)
- DoubleConversion (1.1.6)
- FBLazyVector (0.73.6)
@ -944,6 +946,10 @@ PODS:
- React-Mapbuffer (0.73.6):
- glog
- React-debug
- react-native-safe-area-context (4.9.0):
- React-Core
- react-native-sqlite-storage (6.0.1):
- React-Core
- React-nativeconfig (0.73.6)
- React-NativeModulesApple (0.73.6):
- glog
@ -1111,11 +1117,24 @@ PODS:
- React-jsi (= 0.73.6)
- React-logger (= 0.73.6)
- React-perflogger (= 0.73.6)
- RNGestureHandler (2.15.0):
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
- RNScreens (3.29.0):
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
- RNVectorIcons (10.0.3):
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
- SocketRocket (0.6.1)
- Yoga (1.14.0)
DEPENDENCIES:
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
@ -1167,6 +1186,8 @@ DEPENDENCIES:
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- react-native-sqlite-storage (from `../node_modules/react-native-sqlite-storage`)
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@ -1187,6 +1208,9 @@ DEPENDENCIES:
- React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`)
- React-utils (from `../node_modules/react-native/ReactCommon/react/utils`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
@ -1208,6 +1232,8 @@ SPEC REPOS:
EXTERNAL SOURCES:
boost:
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
BVLinearGradient:
:path: "../node_modules/react-native-linear-gradient"
DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
FBLazyVector:
@ -1261,6 +1287,10 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/logger"
React-Mapbuffer:
:path: "../node_modules/react-native/ReactCommon"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-sqlite-storage:
:path: "../node_modules/react-native-sqlite-storage"
React-nativeconfig:
:path: "../node_modules/react-native/ReactCommon"
React-NativeModulesApple:
@ -1301,11 +1331,18 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/react/utils"
ReactCommon:
:path: "../node_modules/react-native/ReactCommon"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNScreens:
:path: "../node_modules/react-native-screens"
RNVectorIcons:
:path: "../node_modules/react-native-vector-icons"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
boost: d3f49c53809116a5d38da093a8aa78bf551aed09
BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953
FBLazyVector: f64d1e2ea739b4d8f7e4740cde18089cd97fe864
@ -1344,6 +1381,8 @@ SPEC CHECKSUMS:
React-jsinspector: 85583ef014ce53d731a98c66a0e24496f7a83066
React-logger: 3eb80a977f0d9669468ef641a5e1fabbc50a09ec
React-Mapbuffer: 84ea43c6c6232049135b1550b8c60b2faac19fab
react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b
react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261
React-nativeconfig: b4d4e9901d4cabb57be63053fd2aa6086eb3c85f
React-NativeModulesApple: cd26e56d56350e123da0c1e3e4c76cb58a05e1ee
React-perflogger: 5f49905de275bac07ac7ea7f575a70611fa988f2
@ -1364,9 +1403,12 @@ SPEC CHECKSUMS:
React-runtimescheduler: 9636eee762c699ca7c85751a359101797e4c8b3b
React-utils: d16c1d2251c088ad817996621947d0ac8167b46c
ReactCommon: 2aa35648354bd4c4665b9a5084a7d37097b89c10
RNGestureHandler: 67fb54b3e6ca338a8044e85cd6f340265aa41091
RNScreens: 17e2f657f1b09a71ec3c821368a04acbb7ebcb46
RNVectorIcons: 73ab573085f65a572d3b6233e68996d4707fd505
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Yoga: d17d2cc8105eed528474683b42e2ea310e1daf61
Yoga: 805bf71192903b20fc14babe48080582fee65a80
PODFILE CHECKSUM: 29407bd75db4abdbd07d3a8c1b06fc5c12f802b3
PODFILE CHECKSUM: fbd48ab9bd2d7cd12f0450d3da23a3d3a87ccb0b
COCOAPODS: 1.14.3
COCOAPODS: 1.15.2

View File

@ -10,8 +10,21 @@
"test": "jest"
},
"dependencies": {
"@react-navigation/bottom-tabs": "^6.5.19",
"@react-navigation/native": "^6.1.16",
"@react-navigation/stack": "^6.3.28",
"i18next": "^23.10.1",
"i18next-browser-languagedetector": "^7.2.0",
"i18next-http-backend": "^2.5.0",
"react": "18.2.0",
"react-native": "0.73.6"
"react-i18next": "^14.1.0",
"react-native": "0.73.6",
"react-native-gesture-handler": "^2.15.0",
"react-native-linear-gradient": "^2.8.3",
"react-native-safe-area-context": "^4.9.0",
"react-native-screens": "^3.29.0",
"react-native-sqlite-storage": "^6.0.1",
"react-native-vector-icons": "^10.0.3"
},
"devDependencies": {
"@babel/core": "^7.20.0",
@ -22,6 +35,7 @@
"@react-native/metro-config": "0.73.5",
"@react-native/typescript-config": "0.73.1",
"@types/react": "^18.2.6",
"@types/react-native-vector-icons": "^6.4.18",
"@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.6.3",
"eslint": "^8.19.0",

21
src/App.tsx Normal file
View File

@ -0,0 +1,21 @@
import React, {useEffect} from 'react';
import AppNavigator from './AppNavigator';
import {initializeDatabase} from './utils/DatabaseService.js';
import DatabaseTestPage from './pages/DatabaseServiceTest.js';
import BottomTabNavigator from './components/BottomTabNavigator.tsx';
function App(): React.JSX.Element {
useEffect(() => {
initializeDatabase()
.then(_db => {
console.log('Database ready');
})
.catch(_error => {
console.error('Database failed to initialize');
});
}, []);
//return <AppNavigator />;
return <BottomTabNavigator />;
}
export default App;

24
src/AppNavigator.tsx Normal file
View File

@ -0,0 +1,24 @@
// src/AppNavigator.tsx
import React from 'react';
import {createStackNavigator} from '@react-navigation/stack';
import {NavigationContainer} from '@react-navigation/native';
import LoginPage from './pages/LoginPage';
const Stack = createStackNavigator();
const AppNavigator = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login">
<Stack.Screen
name="Login"
component={LoginPage}
options={{title: 'Login'}}
/>
{/* 添加其他页面的路由 */}
</Stack.Navigator>
</NavigationContainer>
);
};
export default AppNavigator;

View File

@ -0,0 +1,63 @@
// BottomTabNavigator.tsx
import React from 'react';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {NavigationContainer} from '@react-navigation/native';
import Icon from 'react-native-vector-icons/Ionicons';
import {useTranslation} from 'react-i18next';
import {RootTabParamList} from './type.ts';
import OverviewPage from '../pages/OverviewPage';
import SubscriptionPage from '../pages/SubscriptionPage';
import SavingPage from '../pages/SavingPage';
import ProfilePage from '../pages/ProfilePage';
const Tab = createBottomTabNavigator<RootTabParamList>(); // 使用类型
const BottomTabNavigator: React.FC = () => {
const {t} = useTranslation();
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({route}) => ({
tabBarLabel: t(route.name), // 使用翻译函数获取标签
tabBarIcon: ({focused, color, size}) => {
let iconName: string;
switch (route.name) {
case 'Overview':
iconName = focused ? 'home' : 'home-outline';
break;
case 'Subscription':
iconName = focused ? 'list' : 'list-outline';
break;
case 'Saving':
iconName = focused ? 'wallet' : 'wallet-outline';
break;
case 'Profile':
iconName = focused ? 'person' : 'person-outline';
break;
default:
iconName = 'ios-alert';
break;
}
console.log(iconName, size, color);
return <Icon name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: 'tomato',
tabBarInactiveTintColor: 'gray',
})}>
<Tab.Screen name="Overview" component={OverviewPage} />
<Tab.Screen
name="Subscription"
component={SubscriptionPage}
options={{headerShown: false}}
/>
<Tab.Screen name="Saving" component={SavingPage} />
<Tab.Screen name="Profile" component={ProfilePage} />
</Tab.Navigator>
</NavigationContainer>
);
};
export default BottomTabNavigator;

7
src/components/type.ts Normal file
View File

@ -0,0 +1,7 @@
// types.ts
export type RootTabParamList = {
Overview: undefined;
Subscription: undefined;
Saving: undefined;
Profile: undefined;
};

20
src/i18n/i18n.js Normal file
View File

@ -0,0 +1,20 @@
import i18n from 'i18next';
import {initReactI18next} from 'react-i18next';
import en from './locales/en.json';
import zh from './locales/zh.json';
i18n
.use(initReactI18next) // 将 i18next 传递给 react-i18next
.init({
resources: {
en: {translation: en},
zh: {translation: zh},
},
lng: 'en', // 默认语言
fallbackLng: 'en',
interpolation: {
escapeValue: false,
},
});
export default i18n;

8
src/i18n/locales/en.json Normal file
View File

@ -0,0 +1,8 @@
{
"welcome": "Welcome to MyAccountApp",
"login": "Log In",
"Overview": "Overview",
"Subscription": "Subscription",
"Saving": "Saving",
"Profile": "Profile"
}

8
src/i18n/locales/zh.json Normal file
View File

@ -0,0 +1,8 @@
{
"welcome": "欢迎使用 MyAccountApp",
"login": "登录",
"Overview": "概览",
"Subscription": "订阅",
"Saving": "存钱",
"Profile": "我的"
}

View File

@ -0,0 +1,128 @@
// src/pages/DatabaseTestPage.js
import React, {useState, useEffect} from 'react';
import {
View,
Text,
TextInput,
Button,
ScrollView,
StyleSheet,
} from 'react-native';
import {initializeDatabase} from '../utils/DatabaseService';
const DatabaseTestPage = () => {
const [db, setDb] = useState(null);
const [users, setUsers] = useState([]);
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
useEffect(() => {
initializeDatabase().then(database => {
setDb(database);
fetchUsers(database);
});
}, []);
const fetchUsers = database => {
database.transaction(tx => {
tx.executeSql('SELECT * FROM Users', [], (tx, results) => { m
const fetchedUsers = [];
for (let i = 0; i < results.rows.length; i++) {
fetchedUsers.push(results.rows.item(i));
}
setUsers(fetchedUsers);
});
});
};
const addUser = () => {
if (db && username && email) {
db.transaction(tx => {
tx.executeSql(
'INSERT INTO Users (username, email, created_at, updated_at) VALUES (?, ?, datetime("now"), datetime("now"))',
[username, email],
() => {
fetchUsers(db);
setUsername('');
setEmail('');
},
);
});
}
};
const deleteUser = userId => {
if (db) {
db.transaction(tx => {
tx.executeSql('DELETE FROM Users WHERE id = ?', [userId], () => {
fetchUsers(db);
});
});
}
};
return (
<ScrollView style={styles.container}>
<View style={styles.form}>
<TextInput
style={styles.input}
placeholder="Username"
value={username}
onChangeText={setUsername}
/>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={setEmail}
/>
<Button title="Add User" onPress={addUser} />
</View>
<View style={styles.usersList}>
{users.map(user => (
<View key={user.id} style={styles.userItem}>
<Text style={styles.userText}>
{user.id}: {user.username} - {user.email}
</Text>
<Button title="Delete" onPress={() => deleteUser(user.id)} />
</View>
))}
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
marginTop: 50,
},
form: {
marginBottom: 20,
},
input: {
marginBottom: 10,
borderWidth: 1,
borderColor: '#ccc',
paddingHorizontal: 10,
paddingVertical: 5,
},
usersList: {
borderTopWidth: 1,
borderColor: '#ccc',
},
userItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 10,
borderBottomWidth: 1,
borderColor: '#ccc',
},
userText: {
fontSize: 16,
},
});
export default DatabaseTestPage;

View File

@ -0,0 +1,20 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const OverviewPage: React.FC = () => {
return (
<View style={styles.container}>
<Text>Overview Page</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default OverviewPage;

20
src/pages/ProfilePage.tsx Normal file
View File

@ -0,0 +1,20 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const ProfilePage: React.FC = () => {
return (
<View style={styles.container}>
<Text>Profile Page</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default ProfilePage;

20
src/pages/SavingPage.tsx Normal file
View File

@ -0,0 +1,20 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const SavingPage: React.FC = () => {
return (
<View style={styles.container}>
<Text>Saving Page</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default SavingPage;

View File

@ -0,0 +1,188 @@
import React, {useState, useEffect} from 'react';
import {View, Text, StyleSheet, FlatList, TouchableOpacity} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import LinearGradient from 'react-native-linear-gradient';
type Subscription = {
id: number;
name: string;
amount: number;
startDate: string;
endDate: string;
color: string[];
};
const mockSubscriptions: Subscription[] = [
{
id: 1,
name: 'Netflix',
amount: 12.99,
startDate: '2021-01-01',
endDate: '2022-01-01',
color: ['#e52d27', '#b31217'],
},
{
id: 2,
name: 'Spotify',
amount: 9.99,
startDate: '2021-02-01',
endDate: '2022-02-01',
color: ['#1db954', '#1ed760'],
},
{
id: 3,
name: 'Amazon Prime',
amount: 14.99,
startDate: '2021-03-01',
endDate: '2022-03-01',
color: ['#f90', '#ff9900'],
},
{
id: 4,
name: 'Disney+',
amount: 7.99,
startDate: '2021-04-01',
endDate: '2022-04-01',
color: ['#113ccf', '#5e91f2'],
},
// 更多示例数据...
];
const SubscriptionPage: React.FC = () => {
const [subscriptions, setSubscriptions] =
useState<Subscription[]>(mockSubscriptions);
const fetchSubscriptions = () => {
setSubscriptions(mockSubscriptions);
};
useEffect(() => {
fetchSubscriptions();
}, []);
const renderItem = ({item}: {item: Subscription}) => {
return (
<LinearGradient
colors={item.color}
style={styles.card}
start={{x: 0, y: 0}}
end={{x: 1, y: 1}}>
<View style={styles.iconPlaceholder} />
<View style={styles.textContainer}>
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.date}>{item.endDate}</Text>
</View>
<Text style={styles.amount}>${item.amount}</Text>
<TouchableOpacity style={styles.editButton}>
<Icon name="pencil" size={20} color="#fff" />
</TouchableOpacity>
</LinearGradient>
);
};
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>Subscriptions</Text>
</View>
<FlatList
contentContainerStyle={styles.listContainer}
data={subscriptions}
renderItem={renderItem}
keyExtractor={item => item.id.toString()}
numColumns={2}
columnWrapperStyle={styles.row}
/>
<TouchableOpacity style={styles.addButton}>
<Icon name="add" size={30} color="#fff" />
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f7f7f7',
},
listContainer: {
paddingHorizontal: 16,
},
row: {
justifyContent: 'space-between',
marginBottom: 16,
},
card: {
borderRadius: 15,
padding: 15,
width: '48%',
shadowColor: '#000',
shadowOpacity: 0.1,
shadowRadius: 10,
shadowOffset: {width: 0, height: 4},
elevation: 5,
},
iconPlaceholder: {
width: 50,
height: 50,
borderRadius: 25,
backgroundColor: '#fff',
justifyContent: 'center',
alignItems: 'center',
},
textContainer: {
flexDirection: 'column',
marginTop: 10,
},
name: {
fontSize: 18,
fontWeight: 'bold',
color: '#fff',
},
date: {
fontSize: 14,
color: '#fff',
marginTop: 5,
},
amount: {
fontSize: 16,
fontWeight: 'bold',
color: '#fff',
position: 'absolute',
top: 10,
right: 10,
},
editButton: {
position: 'absolute',
bottom: 10,
right: 10,
},
addButton: {
backgroundColor: '#4F8EF7',
position: 'absolute',
bottom: 30,
right: 30,
width: 50,
height: 50,
borderRadius: 25,
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.2,
shadowRadius: 8,
elevation: 5,
},
header: {
paddingTop: 70,
paddingBottom: 15,
paddingLeft: 15,
alignItems: 'flex-start',
},
headerTitle: {
fontSize: 27,
fontWeight: 'bold',
color: '#333',
},
});
export default SubscriptionPage;

View File

@ -0,0 +1,66 @@
// DatabaseService.js
import SQLite from 'react-native-sqlite-storage';
SQLite.enablePromise(true);
const databaseName = 'MyAccountApp.db';
const databaseVersion = '1.0';
const databaseDisplayName = 'My Account App Database';
const databaseSize = 200000; // 大约 200KB
async function initializeDatabase() {
try {
const db = await SQLite.openDatabase(
databaseName,
databaseVersion,
databaseDisplayName,
databaseSize,
);
await db.transaction(async tx => {
// 创建 Users 表
await tx.executeSql(
'CREATE TABLE IF NOT EXISTS Users (id INTEGER PRIMARY KEY AUTOINCREMENT, username VARCHAR(30), password VARCHAR(30), email VARCHAR(30), created_at DATETIME, updated_at DATETIME)',
);
// 创建 Accounts 表
await tx.executeSql(
'CREATE TABLE IF NOT EXISTS Accounts (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, name VARCHAR(30), currency VARCHAR(10), balance DECIMAL, created_at DATETIME, updated_at DATETIME)',
);
// 创建 Ledgers 表
await tx.executeSql(
'CREATE TABLE IF NOT EXISTS Ledgers (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, name VARCHAR(30), created_at DATETIME, updated_at DATETIME)',
);
// 创建 Transactions 表
await tx.executeSql(
'CREATE TABLE IF NOT EXISTS Transactions (id INTEGER PRIMARY KEY AUTOINCREMENT, ledger_id INTEGER, account_id INTEGER, type VARCHAR(10), amount DECIMAL, currency VARCHAR(10), date DATE, description VARCHAR(255), created_at DATETIME, updated_at DATETIME)',
);
// 创建 FixedExpenses 表
await tx.executeSql(
'CREATE TABLE IF NOT EXISTS FixedExpenses (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, name VARCHAR(30), amount DECIMAL, frequency VARCHAR(10), created_at DATETIME, updated_at DATETIME)',
);
// 创建 Subscriptions 表
await tx.executeSql(
'CREATE TABLE IF NOT EXISTS Subscriptions (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, name VARCHAR(30), amount DECIMAL, start_date DATE, end_date DATE, created_at DATETIME, updated_at DATETIME)',
);
// 创建 AAPayments 表
await tx.executeSql(
'CREATE TABLE IF NOT EXISTS AAPayments (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, event VARCHAR(30), total_amount DECIMAL, participants INTEGER, paid_by INTEGER, created_at DATETIME, updated_at DATETIME)',
);
// 其他表的创建语句...
});
console.log('Database initialized');
return db;
} catch (error) {
console.error('Error initializing database:', error);
}
}
export {initializeDatabase};

2207
yarn.lock

File diff suppressed because it is too large Load Diff