Задание
Разработать приложение - сайт магазина по продаже игр виртуальной реальности (Тильда НЕ ИСПОЛЬЗОВАТЬ!!!)
Функционал программы
- добавление игр через админ панель
- удаление игр через админ панель
- редактирование игр
- оформление заказа и его оплата
Стэк технологий:
- Бэкенд - язык Python и Django Rest Framework
- Фронтенд - React + Vite
Фрагмент программного кода (бэкенд)
from django.db import models # Create your models here. class Game(models.Model): objects = models.Manager() title = models.CharField(max_length=100, verbose_name="Название игры") description = models.TextField(verbose_name="Описание игры") GENRE_CHOICES = [ ('Все жанры', 'Все жанры'), ('Симуляторы', 'Симуляторы'), ('Шутеры', 'Шутеры'), ('RPG', 'RPG'), ('Хоррор', 'Хоррор'), ] genre = models.CharField(max_length=50, choices=GENRE_CHOICES, verbose_name='Категория', null=True, blank=True, default='Все жанры') price = models.DecimalField(verbose_name="Цена игры", max_digits=10, decimal_places=2) photo = models.ImageField(upload_to='uploads/images', verbose_name="Фото игры", null=True, blank=True) def __str__(self): return self.title class Meta: verbose_name = 'Игра' verbose_name_plural = 'Игры' class Order(models.Model): name = models.CharField(max_length=100, verbose_name="Имя клиента") phone = models.CharField(max_length=20, verbose_name="Телефон") game = models.ForeignKey(Game, on_delete=models.CASCADE, verbose_name="Выбранная игра") created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата заказа") def __str__(self): return f"Заказ от {self.name} - {self.game.title}" class Meta: verbose_name = "Заказ" verbose_name_plural = "Заказы" from django.db import models class Payment(models.Model): card_number = models.CharField(max_length=19, verbose_name="Номер карты") card_holder = models.CharField(max_length=100, verbose_name="Имя владельца") expiry = models.CharField(max_length=5, verbose_name="Срок действия (MM/YY)") cvv = models.CharField(max_length=4, verbose_name="CVV/CVC") created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания") class Meta: verbose_name = "Платёж" verbose_name_plural = "Платежи" def __str__(self): return f"{self.card_holder} — ****{self.card_number[-4:]}"
Фрагмент программного кода (фронтенд)
import React, { useState, useEffect } from 'react'; import GameModal from './GameModal'; // Импортируем новый компонент import axios from 'axios'; // Импортируем axios для запросов к API const Games = ({ setSelectedGame }) => { const [games, setGames] = useState([]); const [selectedGenre, setSelectedGenre] = useState('all'); const [modalGame, setModalGame] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const genres = ['Все жанры', 'Симуляторы', 'Шутеры', 'RPG', 'Хоррор']; useEffect(() => { const fetchGames = async () => { try { const response = await axios.get('http://127.0.0.1:8000/api/games'); setGames(response.data); setLoading(false); } catch (error) { setError('Ошибка при загрузке игр:', error); setLoading(false); } }; fetchGames(); }, []); const filteredGames = selectedGenre === 'all' ? games : games.filter(game => game.genre === selectedGenre); const handleGenreClick = (genre) => { setSelectedGenre(genre === 'Все жанры' ? 'all' : genre); }; const openModal = (game) => { setModalGame(game); setSelectedGame(game); // Обновляем состояние selectedGame в компоненте App }; const closeModal = () => { setModalGame(null); }; const scrollToOrderForm = () => { const orderForm = document.getElementById('order'); if (orderForm) { orderForm.scrollIntoView({ behavior: 'smooth' }); } }; return ( <> {loading && <div>Загрузка...</div>} {error && <div>{error}</div>} <section className="games" id="games"> <div className="container"> <h2>Выберите игру</h2> <div className="games-content" id="games-content"> <aside className="games-filter"> <ul id="genre-filter"> {genres.map((genre, index) => ( <li key={index} className={selectedGenre === (genre === 'Все жанры' ? 'all' : genre) ? 'active' : ''} data-genre={genre} onClick={() => handleGenreClick(genre)} > {genre} </li> ))} </ul> </aside> <div className="games-items" id="games-list"> {filteredGames.map(game => ( <article key={game.id} className="game" data-genre={game.genre}> <img src="/images/loading-150-1.gif" data-src={game.photo} alt={game.title} onClick={() => openModal(game)} // Добавляем обработчик клика на изображение /> <div className="game-details"> <h4>{game.title}</h4> <p>{game.description}</p> <div className="game-action"> <ul> <li> <div className="game-price">{game.price} руб.</div> </li> </ul> <a href="#order" className="button white-button" onClick={(e) => { e.preventDefault(); openModal(game); scrollToOrderForm(); // Прокручиваем страницу до формы заказа }} > Заказать </a> </div> </div> </article> ))} </div> </div> </div> </section> <GameModal game={modalGame} onClose={closeModal} /> {/* Используем новый компонент */} </> ); }; export default Games;
Скриншот архива с проектом
Структура проекта (версия 1)
Структура проекта (версия 2)
Пояснения по запуску программы
1. Уставливаем Node.js 2. Устанавливаем Visual Studio Code 3. Устанавливаем все нужные расширения. 4. Запускаем Бэкенд - python manage.py runserver и фронтенд - npm run dev . 5. Для версии 2 только запускаем фронтенд той же командой.
-