Министерство цифрового развития, связи и массовых коммуникаций РФ
ордена Трудового Красного Знамени федеральное государственное
бюджетное образовательное учреждение высшего образования
«МОСКОВСКИЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ СВЯЗИ И
ИНФОРМАТИКИ»
Кафедра «МКиИТ»
Лабораторная № 7
по дисциплине «Разработка кроссплатформенного программного обеспечения»
Реализация CRUD-запросов
Выполнил:
студент группы БФИ2203
Михайлов К. А.
Проверил:
Потапченко Т.Д.
Москва, 2024 г.
Цель работы:
В рамках этой лабораторной работы вам нужно будет модифицировать функционал корзины товаров, реализованного в лабораторной работе №6. Также научимся работать с GitHub Pages.
Требования к работе:
Работа с внешним API заказов(например, https://jsonplaceholder.typicode.com, https://dummyjson.com или любое другое подходящее). API должно поддерживать операции:
POST-запрос — для отправки заказа на сервер и получения его номера.
GET-запрос — для получения истории заказов и отображения их в приложении.
DELETE-запрос — для удаления заказа из истории.
Страница оформления заказа:
Интеграция с картой для указания места доставки (использовать библиотеку react-yandex-maps для отображения карты, на которой пользователь может указать место доставки)
При оформлении заказа необходимо добавить поле для выбора даты доставки.
Хранение истории заказов:
История заказов будет отображаться в приложении на отдельной странице, и при необходимости можно будет удалить заказ.
Интеграция с AsyncStorage:
Несмотря на работу с API, состояние корзины должно сохраняться в AsyncStorage, чтобы при перезагрузке приложения данные сохранялись.
Интерфейс пользователя:
Интуитивно понятный интерфейс с возможностью оформления заказов, отображения истории заказов и удаления заказа.
Вывод всех необходимых данных (стоимость, номер заказа, место доставки, дата доставки).
Листинг кода
import React from 'react';
import { useEffect, useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
import Footer from './components/Footer';
import MainScreen from './screens/MainScreen';
import AsyncStorage from '@react-native-async-storage/async-storage';
import axios from 'axios';
import MakeOrderScreen from './screens/MakeOrderScreen';
import OrdersHistoryScreen from './screens/OrdersHistoryScreen';
import OrderDetailsScreen from './screens/OrderDetailsScreen';
const Stack = createStackNavigator();
export default function App() {
const [data, setData] = useState([])
const [deferred, setDeferred] = useState([
// { key: '9', price: 101, quantity: 1, text: 'Товар A' },
// { key: '10', price: 102, quantity: 1, text: 'Товар B' },
]);
const [totalSum, setTotalSum] = useState(0);
const [orders, setOrders] = useState([
]);
useEffect(() => {
const loadData = async () => {
try {
const storedData = await AsyncStorage.getItem('inCart');
const storedDeferred = await AsyncStorage.getItem('deferred');
const storedOrders = await AsyncStorage.getItem('orders');
if (storedData) setData(JSON.parse(storedData));
else {
response = await axios.get('https://my-json-server.typicode.com/stproger/myFakeJSON/inCart');
setData(response.data);
}
if (storedDeferred) setDeferred(JSON.parse(storedDeferred));
else {
response = await axios.get('https://my-json-server.typicode.com/stproger/myFakeJSON/deffered');
setDeferred(response.data);
}
if (storedOrders) setOrders(JSON.parse(storedOrders));
else {
response = await axios.get('https://my-json-server.typicode.com/stproger/myFakeJSON/orders');
setOrders(response.data);
}
} catch (e) {
console.error('Ошибка загрузки данных:', e);
}
};
loadData();
}, []);
useEffect(() => {
const saveData = async () => {
try {
await AsyncStorage.setItem('inCart', JSON.stringify(data));
await AsyncStorage.setItem('deferred', JSON.stringify(deferred));
await AsyncStorage.setItem('orders', JSON.stringify(orders));
} catch (e) {
console.error('Ошибка сохранения данных:', e);
}
try {
console.log('Отправка данных')
const cart_response = await axios.post('https://jsonplaceholder.typicode.com/users/1/posts', data);
const deferred_response = await axios.post('https://jsonplaceholder.typicode.com/users/1/posts', deferred);
const orders_response = await axios.post('https://jsonplaceholder.typicode.com/users/1/posts', orders);
} catch (e) {
console.error('Ошибка отправки данных:', e);
}
};
saveData();
}, [data, deferred]);
return (
<NavigationContainer>
<View style={styles.body}>
<Stack.Navigator>
<Stack.Screen
name="MainScreen"
children={() => (
<MainScreen
data={data}
deferred={deferred}
totalSum={totalSum}
setData={setData}
setDeferred={setDeferred}
setTotalSum={setTotalSum}
/>
)}
options={{ headerShown: false }}
/>
<Stack.Screen
name="MakeOrderScreen"
children={() => (
<MakeOrderScreen data={data} setData={setData} totalSum={totalSum} setOrders={setOrders} orders={orders} />
)}
options={{ headerTitle: "Вернуться к корзине"}}/>
<Stack.Screen name="OrdersHistoryScreen"
children={() => (
<OrdersHistoryScreen orders={orders} />
)}
options={{ headerTitle: "Вернуться к корзине"}}/>
<Stack.Screen name="OrderDetailsScreen" component={OrderDetailsScreen} options={{ title: 'Детали заказа' }} />
</Stack.Navigator>
</View>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
body: {
flex: 1,
flexDirection: 'column',
borderLeftWidth: 4,
borderRightWidth: 4,
borderColor: "#ddd",
marginHorizontal: "auto",
maxWidth: 1000,
width: '100%',
},
header: {
fontSize: "2em",
textAlign: 'center',
marginVertical: 10,
}
});
import React from 'react';
import { useEffect, useState } from 'react';
import { View, Text, StyleSheet, Button, TouchableOpacity } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { NavigationContainer } from '@react-navigation/native';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
import Footer from '../components/Footer';
import CartTab from "../tabs/CartTab";
import AddProductTab from "../tabs/AddProductTab";
import DeferredTab from "../tabs/DeferredTab";
import AsyncStorage from '@react-native-async-storage/async-storage';
import axios from 'axios';
import { ScrollView } from 'react-native-web';
const Tab = createMaterialTopTabNavigator();
export default function MainScreen({data, deferred, totalSum, setData, setDeferred, setTotalSum}) {
const navigation = useNavigation();
const addProduct = (product) => {
setData(prevItems => [...prevItems, product]);
};
useEffect(() => {
const total = data.reduce((sum, item) => sum + item.price * item.quantity, 0)
setTotalSum(total)
}, [data])
const updateQuantity = (key, quantity, type) => {
if (type === "data") {
setData(prevItems =>
prevItems.map(item =>
item.key === key ? { ...item, quantity } : item
)
);
} else if (type === "deferred") {
setDeferred(prevItems =>
prevItems.map(item =>
item.key === key ? { ...item, quantity } : item
)
);
}
};
const deleteItem = (key, type) => {
if (type === "data") {
setData(prevItems =>
prevItems.filter(item => item.key !== key)
);
} else if (type === "deferred") {
setDeferred(prevItems =>
prevItems.filter(item => item.key !== key)
);
}
};
const switchItem = (key, type) => {
const moveProduct = (source, destination, setSource, setDestination) => {
const product = source.find(item => item.key === key);
if (product) {
setDestination(prevItems => [...prevItems, product]);
setSource(prevData => prevData.filter(item => item.key !== key));
}
};
if (type === "data") {
moveProduct(data, deferred, setData, setDeferred);
} else if (type === "deferred") {
moveProduct(deferred, data, setDeferred, setData);
}
};
const clearCart = () => {
setData([]);
}
return (
<View style={styles.container}>
<TouchableOpacity style={styles.historyButton} onPress={() => navigation.navigate("OrdersHistoryScreen")}>
<Text style={styles.historyButtonText}>{"<- К истории заказов"}</Text>
</TouchableOpacity>
<Text style={styles.header}>Корзина</Text>
<Tab.Navigator
screenOptions={{
tabBarActiveTintColor: 'blue',
tabBarLabelStyle: { fontSize: 14, fontWeight: 'bold' },
tabBarStyle: { backgroundColor: 'white' },
}}
>
<Tab.Screen name="Товары к покупке" children={() => (
<ScrollView style={styles.body}>
<CartTab data={data} updateQuantity={updateQuantity} deleteItem={deleteItem} setDeferred={setDeferred} switchItem={switchItem} />
</ScrollView>
)} />
<Tab.Screen name="Отложенные товары" children={() => (
<ScrollView style={styles.body}>
<DeferredTab data={deferred} setData={setData} updateQuantity={updateQuantity} deleteItem={deleteItem} switchItem={switchItem} />
</ScrollView>
)} />
<Tab.Screen name="Добавить товар" children={() => (
<ScrollView style={styles.body}>
<AddProductTab addProduct={addProduct} />
</ScrollView>
)} />
</Tab.Navigator>
<Footer totalSum={totalSum} clearCart={clearCart} ></Footer>
</View>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: "white",
height: "100%"
},
body: {
flex: 1,
flexDirection: 'column',
marginHorizontal: "auto",
width: '100%',
maxHeight: "100%",
},
header: {
fontSize: "2em",
textAlign: 'center',
marginVertical: 10,
},
historyButton: {
paddingHorizontal: 15
},
historyButtonText: {
fontSize: "1.5em",
color: "gray",
textDecorationLine: "underline",
},
});
Import React from 'react';
Import { useEffect, useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, ScrollView } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { YMaps, Map, Placemark } from '@pbe/react-yandex-maps';
export default function MakeOrderScreen({ data, setData, totalSum, setOrders, orders }) {
const navigation = useNavigation();
const [deliveryDate, setDeliveryDate] = useState("");
const [deliveryAddress, setDeliveryAddress] = useState([55.751244, 37.618423]);
const addOrder = () => {
const newOrder = {
key: `${Date.now()}`, // Генерация уникального key с использованием текущего времени
date: deliveryDate,
address: deliveryAddress.join(", "),
products: [...data],
totalSum: totalSum,
}
setOrders(prevItems => [...prevItems, newOrder]);
setData([]);
setDeliveryDate('');
setDeliveryAddress('');
}
const defaultState = {
center: deliveryAddress,
zoom: 10,
};
const handleMapClick = (event) => {
const coords = event.get('coords');
setDeliveryAddress(coords);
};
return (
<ScrollView style={styles.container}>
<Text style={styles.header}>Оформление заказа</Text>
{/* Данные о товарах */}
<View style={styles.orderDetails}>
{data.map(item => (
<View key={item.key} style={styles.item}>
<Text style={styles.itemText}>{item.text}</Text>
<Text>{`Цена: ${item.price} ₽`}</Text>
<Text>{`Количество: ${item.quantity}`}</Text>
</View>
))}
</View>
{/* Сумма заказа */}
<Text style={styles.totalSum}>Итого: {totalSum} ₽</Text>
{/* Выбор даты доставки */}
<Text>Выберите дату доставки:</Text>
<input
type="date"
value={deliveryDate}
onChange={(e) => setDeliveryDate(e.target.value)}
style={{ padding: 10, fontSize: 16, marginBottom: 10 }}
/>
{/* Ввод адреса доставки */}
<Text>Введите адрес доставки:</Text>
<div style={styles.webMap}>
<YMaps>
<Map
defaultState={defaultState}
width="100%"
height="400px"
onClick={handleMapClick}
>
<Placemark geometry={deliveryAddress} />
</Map>
</YMaps>
</div>
<TextInput
style={styles.input}
placeholder="Улица, дом, квартира"
value={deliveryAddress}
onChangeText={setDeliveryAddress}
/>
{/* Кнопка подтверждения заказа */}
<Button
title="Подтвердить заказ"
onPress={() => {
addOrder();
alert(`Заказ подтвержден на адрес: ${deliveryAddress} с датой доставки: ${deliveryDate}`);
navigation.navigate('MainScreen');
}}
/>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: "white",
padding: 20,
},
header: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
},
orderDetails: {
marginBottom: 20,
},
item: {
marginBottom: 10,
borderBottomWidth: 1,
},
itemText: {
fontWeight: 'bold',
},
totalSum: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 20,
},
input: {
height: 40,
borderColor: '#ccc',
borderWidth: 1,
marginBottom: 20,
paddingHorizontal: 10,
},
});
