На нашем сайте Вы сможете найти готовые курсовые и дипломные работы по программированию
Сейчас работаем

Онлайн сервис (АИС) Книгочей

Скриншот архива с проектом

Задание

Разработать приложение для книжного магазина Книгочей для учета книг, продаж и статистики. Рекомендуемый стек - Python Django DRF Vue.js. Приложение должно иметь удобный и привлекательный интерфейс, дашборд-панель с аналитикой. 

 Функционал программы

  • Учет книг
  • Добавление книг
  • Редактирование книг
  • Учет продаж
  • Добавление продаж
  • Редактирование продаж
  • Аналитическая дашборд панель
  • Печать данных продаж

Содержание отчета к программе

  • Введение
  • Задание на курсовую работу
  • Постановка задачи
  • Проектная часть
  • Теоретическая часть
  • Общее описание разработки
  • Таблица свойств объектов
  • Список идентификаторов
  • Структура приложения
  • Функциональное описание приложения
  • Описание работы программы с представлением экранных форм
  • Заключение
  • Список использованных источников
  • Приложение. Исходный код программы с комментариями
  • MainForm.cs
  • PlayForm.cs
  • RulesForm.cs
  • LastGamesForm.cs

Фрагмент программного кода (модели)

from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.core.exceptions import ValidationError
from decimal import Decimal
# Create your models here.
class Book(models.Model):
    objects = models.Manager()
    author = models.CharField(max_length=100, verbose_name="Автор книги")
    name = models.CharField(max_length=100, verbose_name="Наименование книги")
    description = models.TextField(verbose_name="Описание книги")       
    GENRE_CHOICES = [
        ('Ужасы', 'Ужасы'),
        ('Мистика', 'Мистика'),
        ('Фантастика', 'Фантастика'),
        ('Триллер', 'Триллер'),
    ] 
    genre = models.CharField(max_length=50, choices=GENRE_CHOICES, verbose_name='Жанр книги', null=True, blank=True)
    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.name
    
    class Meta:
        verbose_name = 'Книга'
        verbose_name_plural = 'Книги'
    

class Sale(models.Model):
    objects = models.Manager()    
    book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='sales', verbose_name='Проданная книга')
    date = models.DateField(verbose_name='Дата продажи')
    start_quantity = models.PositiveIntegerField(verbose_name="Начальное количество")
    end_quantity = models.PositiveIntegerField(verbose_name="Конечное количество")    

    class Meta:
        verbose_name = 'Продажа'
        verbose_name_plural = 'Продажи'

    def save(self, *args, **kwargs):
        if self.end_quantity < self.start_quantity:
            raise ValidationError("Конечные продажи должны быть больше или равны начальному количеству.")        
        super().save(*args, **kwargs)

    def get_sold_quantity(self):
        """Количество проданных единиц"""
        return int(self.end_quantity) - int(self.start_quantity)
    
    def __str__(self):
        return f"{self.book.name}: {self.date} - {self.get_sold_quantity()} шт"

Фрагмент программного кода (компонент Books)

<script setup>
import { ref, onMounted } from "vue";
import axios from "axios";

const books = ref([]);
const searchQuery = ref("");
const viewMode = ref("cards");

const fetchBooks = async (query = "") => {
  try {
    const url = query
      ? `http://localhost:8000/api/books/?search=${encodeURIComponent(query)}`
      : "http://localhost:8000/api/books/";

    const response = await axios.get(url);
    books.value = response.data.results || response.data;
  } catch (error) {
    console.error("Ошибка при загрузке книг:", error);
  }
};

onMounted(() => {
  fetchBooks();
});

const searchBooks = () => {
  fetchBooks(searchQuery.value);
};

const clearSearch = () => {
  searchQuery.value = "";
  fetchBooks("");
};
</script>

<template>
  <div class="container my-4">
    <h1 class="mb-4">📚 Список книг</h1>

    <!-- кнопки -->
    <div class="mb-3 d-flex gap-2">
      <router-link to="/books/add" class="btn btn-success">
        ➕ Добавить книгу
      </router-link>
      <router-link to="/" class="btn btn-secondary">
        ← На главную
      </router-link>
    </div>

    <!-- поиск с кнопкой очистки -->
    <div class="mb-3 search-container">
      <div class="search-wrapper">
        <input
          v-model="searchQuery"
          @keyup.enter="searchBooks"
          type="text"
          class="form-control search-input"
          placeholder="Поиск по названию или автору"
        />
        <button 
          v-if="searchQuery" 
          @click="clearSearch" 
          class="btn-clear"
          title="Очистить поиск"
        >
          ✕
        </button>
        <button @click="searchBooks" class="btn btn-primary search-btn">
          🔍 Поиск
        </button>
      </div>
    </div>

    <!-- переключатель -->
    <div class="mb-3">
      <button
        class="btn btn-outline-dark me-2"
        :class="{ active: viewMode === 'cards' }"
        @click="viewMode = 'cards'"
      >
        🃏 Карточки
      </button>
      <button
        class="btn btn-outline-dark"
        :class="{ active: viewMode === 'table' }"
        @click="viewMode = 'table'"
      >
        📊 Таблица
      </button>
    </div>

    <!-- ================= TABLE ================= -->
    <table
      v-if="viewMode === 'table'"
      class="table table-hover table-striped align-middle"
    >
      <thead class="table-info">
        <tr>
          <th class="text-center">ID</th>
          <th>Обложка</th>
          <th>Название</th>
          <th>Автор</th>
          <th>Жанр</th>
          <th>Цена</th>
          <th>Действия</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="book in books" :key="book.id">
          <td class="text-center">{{ book.id }}</td>
          <td>
            <img
              v-if="book.photo"
              : src="/images/loading-150-1.gif" data-src="book.photo"
              class="book-thumb"
              :alt="book.name"
            />
            <span v-else class="text-muted">Нет фото</span>
          </td>
          <td class="fw-bold">{{ book.name }}</td>
          <td>{{ book.author }}</td>
          <td>{{ book.genre || '—' }}</td>
          <td class="fw-bold text-success">{{ book.price }} ₽</td>
          <td>
            <router-link
              :to="`/books/${book.id}`"
              class="btn btn-primary btn-sm me-1"
            >
              👁️ Подробнее
            </router-link>
            <router-link
              :to="`/books/${book.id}/edit`"
              class="btn btn-warning btn-sm"
            >
              ✏️ Изменить
            </router-link>
          </td>
        </tr>
      </tbody>
    </table>

    <!-- ================= CARDS ================= -->
    <div v-else class="row">
      <div
        v-for="book in books"
        :key="book.id"
        class="col-md-6 col-lg-4 mb-4"
      >
        <div class="card h-100 book-card">
          <div class="card-img-top-wrapper">
            <img
              v-if="book.photo"
              : src="/images/loading-150-1.gif" data-src="book.photo"
              class="card-img-top book-image"
              :alt="book.name"
            />
            <div v-else class="no-image-placeholder">
              <span>📖</span>
              <p>Нет обложки</p>
            </div>
          </div>

          <div class="card-body d-flex flex-column">
            <h5 class="card-title">{{ book.name }}</h5>
            <p class="card-text text-muted small">{{ book.author }}</p>
            
            <p class="card-text description-text">
              {{ book.description?.slice(0, 100) }}...
            </p>

            <div class="mt-2 mb-2">
              <span class="badge genre-badge">{{ book.genre || 'Без жанра' }}</span>
            </div>

            <div class="price-tag">
              {{ book.price }} ₽
            </div>

            <div class="card-actions mt-3">
              <router-link
                :to="`/books/${book.id}`"
                class="btn btn-outline-primary btn-sm"
              >
                📖 Подробнее
              </router-link>
              <router-link
                :to="`/books/${book.id}/edit`"
                class="btn btn-outline-warning btn-sm"
              >
                ✏️ Редактировать
              </router-link>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.container {
  max-width: 1300px;
  margin: 0 auto;
}

/* Стили для поиска */
.search-container {
  position: relative;
}

.search-wrapper {
  display: flex;
  position: relative;
  gap: 10px;
  align-items: center;
}

.search-input {
  flex: 1;
  padding-right: 45px;
}

.btn-clear {
  position: absolute;
  right: 115px;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  color: #999;
  cursor: pointer;
  font-size: 18px;
  padding: 5px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  width: 28px;
  height: 28px;
  transition: all 0.2s;
  z-index: 1;
}

.btn-clear:hover {
  background: #e0e0e0;
  color: #333;
}

.search-btn {
  white-space: nowrap;
}

/* Карточки */
.book-card {
  border-radius: 16px;
  overflow: hidden;
  transition: all 0.3s ease;
  border: 1px solid #e9ecef;
  background: white;
}

.book-card:hover {
  transform: translateY(-8px);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.12);
}

.card-img-top-wrapper {
  background: #f8f9fa;
  padding: 20px;
  text-align: center;
  border-bottom: 1px solid #e9ecef;
}

.book-image {
  max-width: 100%;
  height: 200px;
  object-fit: contain;
  border-radius: 8px;
}

.no-image-placeholder {
  height: 200px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #f8f9fa;
  color: #adb5bd;
}

.no-image-placeholder span {
  font-size: 48px;
  margin-bottom: 8px;
}

.no-image-placeholder p {
  margin: 0;
  font-size: 14px;
}

.card-body {
  padding: 20px;
}

.card-title {
  font-size: 1.2rem;
  font-weight: 600;
  margin-bottom: 8px;
  color: #333;
  line-height: 1.3;
}

.description-text {
  color: #6c757d;
  font-size: 0.85rem;
  line-height: 1.4;
  margin-bottom: 12px;
  flex-grow: 1;
}

.genre-badge {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  padding: 6px 12px;
  border-radius: 20px;
  font-size: 0.75rem;
  font-weight: 500;
}

.price-tag {
  font-size: 1.5rem;
  font-weight: 700;
  color: #28a745;
  margin: 12px 0;
}

.card-actions {
  display: flex;
  gap: 10px;
  margin-top: auto;
}

.card-actions .btn {
  flex: 1;
  padding: 8px 12px;
  font-size: 0.85rem;
  border-radius: 8px;
  transition: all 0.2s;
}

.btn-outline-primary {
  border: 2px solid #667eea;
  color: #667eea;
  background: white;
}

.btn-outline-primary:hover {
  background: #667eea;
  color: white;
  transform: translateY(-2px);
}

.btn-outline-warning {
  border: 2px solid #ffc107;
  color: #856404;
  background: white;
}

.btn-outline-warning:hover {
  background: #ffc107;
  color: white;
  transform: translateY(-2px);
}

.book-thumb {
  width: 60px;
  height: 60px;
  object-fit: cover;
  border-radius: 8px;
}

.table {
  background: white;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}

.table thead th {
  background: #f8f9fa;
  border-bottom: 2px solid #dee2e6;
  font-weight: 600;
}

.table tbody tr:hover {
  background: #f8f9ff;
}

.btn.active {
  background-color: #667eea;
  color: white;
  border-color: #667eea;
}

@media (max-width: 768px) {
  .card-actions {
    flex-direction: column;
  }
  
  .book-image {
    height: 150px;
  }
  
  .price-tag {
    font-size: 1.2rem;
  }
  
  .search-wrapper {
    flex-wrap: wrap;
  }
  
  .search-input {
    width: 100%;
  }
  
  .btn-clear {
    right: 20px;
  }
  
  .search-btn {
    width: 100%;
  }
}
</style>

Пояснения по запуску программы

1. Скачиваем Visual Studio Code/ 2. Настраиваем расширения для работы с Python и Vue. 3. Скачиваем интерпретатор Python, при установке незабываем ставить галочку PATH. 4. Скачиваем и устанавливаем node.js. 5. Открываем проект в Visual Studio - устанавливаем модули Python из файла. 6. В папке Фронтенд - клиент - устанавливаем удаленные иодули node. 7. Создаем два терминала в студии. в одном запускаем бэкенд - cd Backend - cd server - python manage.py runserver. dj  втором запускаем клиент - cd Frontend - cd client - npm run dev. Пользуемся приложением. А для входа в админ панель Django потребуется создать права суперпользователя . Но я вам сразу дам данные - Логин: Admin Пароль:    E9pJqf9CG.Twx45

Купить 5000,00 
Сразу после оплаты Вы сможете скачать работу и мы вышлем дополнительно файл с работой на электронную почту. Исходник программ Вы сможете отредактировать, как Вам нужно.
Комментарии (0)
Онлайн сервис (АИС) Книгочей

/ /

Оставить комментарий

Ты не можешь комментировать

Только зарегистрированые пользователи имеют возможность комментировать работы
Купить

5000,00 

Покупается впервые!
Сразу после оплаты Вы сможете скачать работу и мы вышлем дополнительно файл с работой на электронную почту. Исходник программ Вы сможете отредактировать, как Вам нужно.

Заказать через

АИС книжного магазина Книгочей.zip
40956301
Оцени работу

рейтинг

Онлайн сервис (АИС) Книгочей
Онлайн сервис (АИС) Книгочей - приложение для магазина Книгочей, предначначено для учета книг, продаж и сбора аналитики - количестве книг, количестве продаж, общих продажах, жанрах книг.
Категория: Образование
Стоимость: 5000,00