After Graduate Update
This commit is contained in:
parent
b92a91ab37
commit
c6917dd85e
69 changed files with 7540 additions and 0 deletions
574
ressult/gui/main_window
Normal file
574
ressult/gui/main_window
Normal file
|
|
@ -0,0 +1,574 @@
|
|||
# gui/main_window.py
|
||||
"""
|
||||
Главное окно приложения PyQt6 с поддержкой авторизации
|
||||
"""
|
||||
import sys
|
||||
import requests
|
||||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
||||
QHBoxLayout, QLabel, QPushButton, QListWidget,
|
||||
QListWidgetItem, QMessageBox, QFrame, QStackedWidget,
|
||||
QMenuBar, QMenu, QStatusBar, QToolBar)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
from PyQt6.QtGui import QFont, QPixmap, QIcon, QAction
|
||||
from .partner_form import PartnerForm
|
||||
from .sales_history import SalesHistoryWindow
|
||||
from .material_calculator import MaterialCalculatorWindow
|
||||
|
||||
class PartnerCard(QFrame):
|
||||
"""Карточка партнера для отображения в списке"""
|
||||
partner_clicked = pyqtSignal(dict)
|
||||
|
||||
def __init__(self, partner_data):
|
||||
super().__init__()
|
||||
self.partner_data = partner_data
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
self.setStyleSheet("""
|
||||
PartnerCard {
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin: 4px;
|
||||
}
|
||||
PartnerCard:hover {
|
||||
background-color: #f5f5f5;
|
||||
border-color: #007acc;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(8, 8, 8, 8)
|
||||
layout.setSpacing(4)
|
||||
|
||||
# Заголовок с типом и названием
|
||||
header_layout = QHBoxLayout()
|
||||
header_layout.setSpacing(4)
|
||||
|
||||
type_label = QLabel(f"{self.partner_data.get('partner_type', 'Тип не указан')} |")
|
||||
type_label.setStyleSheet("color: #666; font-weight: bold;")
|
||||
|
||||
name_label = QLabel(self.partner_data['company_name'])
|
||||
name_label.setStyleSheet("font-weight: bold; font-size: 14px;")
|
||||
name_label.setWordWrap(True)
|
||||
|
||||
# Безопасное преобразование рейтинга
|
||||
rating_value = self.partner_data.get('rating', 0)
|
||||
if isinstance(rating_value, float):
|
||||
rating_value = int(rating_value)
|
||||
|
||||
rating_label = QLabel(f"{rating_value}%")
|
||||
rating_label.setStyleSheet("color: #007acc; font-weight: bold;")
|
||||
|
||||
header_layout.addWidget(type_label)
|
||||
header_layout.addWidget(name_label)
|
||||
header_layout.addStretch()
|
||||
header_layout.addWidget(rating_label)
|
||||
|
||||
# Информация о директоре
|
||||
director_label = QLabel(self.partner_data.get('director_name', 'Директор не указан'))
|
||||
director_label.setStyleSheet("color: #444;")
|
||||
|
||||
# Контактная информация
|
||||
phone_label = QLabel(self.partner_data.get('phone', 'Телефон не указан'))
|
||||
phone_label.setStyleSheet("color: #666;")
|
||||
|
||||
layout.addLayout(header_layout)
|
||||
layout.addWidget(director_label)
|
||||
layout.addWidget(phone_label)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""Обработка клика на карточке"""
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
self.partner_clicked.emit(self.partner_data)
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
"""Главное окно приложения с поддержкой авторизации"""
|
||||
|
||||
def __init__(self, user_data):
|
||||
super().__init__()
|
||||
self.user_data = user_data
|
||||
self.current_partner = None
|
||||
self.auth = user_data.get('auth')
|
||||
self.setup_ui()
|
||||
self.load_partners()
|
||||
|
||||
def setup_ui(self):
|
||||
"""Настройка интерфейса главного окна"""
|
||||
self.setWindowTitle(f"MasterPol - Система управления партнерами")
|
||||
self.setGeometry(100, 100, 1200, 700)
|
||||
|
||||
# Установка иконки приложения
|
||||
try:
|
||||
self.setWindowIcon(QIcon("resources/icon.png"))
|
||||
except:
|
||||
pass
|
||||
|
||||
# Создание меню
|
||||
self.create_menu()
|
||||
|
||||
# Создание тулбара
|
||||
self.create_toolbar()
|
||||
|
||||
# Создание статусной строки
|
||||
self.create_statusbar()
|
||||
|
||||
# Центральный виджет
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
main_layout = QHBoxLayout()
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# Левая панель - список партнеров
|
||||
left_panel = self.create_partners_panel()
|
||||
main_layout.addWidget(left_panel, 1)
|
||||
|
||||
# Правая панель - детальная информация
|
||||
self.right_panel = self.create_details_panel()
|
||||
main_layout.addWidget(self.right_panel, 2)
|
||||
|
||||
central_widget.setLayout(main_layout)
|
||||
|
||||
def create_menu(self):
|
||||
"""Создание меню приложения"""
|
||||
menubar = self.menuBar()
|
||||
|
||||
# Меню Файл
|
||||
file_menu = menubar.addMenu('Файл')
|
||||
|
||||
refresh_action = QAction('Обновить', self)
|
||||
refresh_action.setShortcut('F5')
|
||||
refresh_action.triggered.connect(self.load_partners)
|
||||
file_menu.addAction(refresh_action)
|
||||
|
||||
file_menu.addSeparator()
|
||||
|
||||
logout_action = QAction('Выход', self)
|
||||
logout_action.setShortcut('Ctrl+Q')
|
||||
logout_action.triggered.connect(self.logout)
|
||||
file_menu.addAction(logout_action)
|
||||
|
||||
# Меню Сервис
|
||||
service_menu = menubar.addMenu('Сервис')
|
||||
|
||||
calc_action = QAction('Калькулятор материалов', self)
|
||||
calc_action.triggered.connect(self.show_material_calculator)
|
||||
service_menu.addAction(calc_action)
|
||||
|
||||
# Меню Справка
|
||||
help_menu = menubar.addMenu('Справка')
|
||||
|
||||
about_action = QAction('О программе', self)
|
||||
about_action.triggered.connect(self.show_about)
|
||||
help_menu.addAction(about_action)
|
||||
|
||||
def create_toolbar(self):
|
||||
"""Создание панели инструментов"""
|
||||
toolbar = QToolBar("Основные инструменты")
|
||||
self.addToolBar(toolbar)
|
||||
|
||||
refresh_action = QAction('Обновить', self)
|
||||
refresh_action.triggered.connect(self.load_partners)
|
||||
toolbar.addAction(refresh_action)
|
||||
|
||||
toolbar.addSeparator()
|
||||
|
||||
add_partner_action = QAction('Добавить партнера', self)
|
||||
add_partner_action.triggered.connect(self.show_add_partner_form)
|
||||
toolbar.addAction(add_partner_action)
|
||||
|
||||
def create_statusbar(self):
|
||||
"""Создание статусной строки"""
|
||||
statusbar = self.statusBar()
|
||||
user_info = f"Пользователь: {self.user_data.get('full_name', 'Неизвестно')}"
|
||||
statusbar.showMessage(user_info)
|
||||
|
||||
def create_partners_panel(self):
|
||||
"""Создание панели списка партнеров"""
|
||||
panel = QWidget()
|
||||
panel.setMaximumWidth(400)
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Заголовок
|
||||
title = QLabel("Партнеры")
|
||||
title.setFont(QFont("Arial", 16, QFont.Weight.Bold))
|
||||
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
title.setStyleSheet("padding: 10px;")
|
||||
layout.addWidget(title)
|
||||
|
||||
# Панель управления
|
||||
control_layout = QHBoxLayout()
|
||||
control_layout.setSpacing(10)
|
||||
|
||||
self.add_button = QPushButton("Добавить партнера")
|
||||
self.add_button.clicked.connect(self.show_add_partner_form)
|
||||
self.add_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
|
||||
self.refresh_button = QPushButton("Обновить")
|
||||
self.refresh_button.clicked.connect(self.load_partners)
|
||||
self.refresh_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #545b62;
|
||||
}
|
||||
""")
|
||||
|
||||
control_layout.addWidget(self.add_button)
|
||||
control_layout.addWidget(self.refresh_button)
|
||||
control_layout.addStretch()
|
||||
|
||||
layout.addLayout(control_layout)
|
||||
|
||||
# Список партнеров
|
||||
self.partners_list = QListWidget()
|
||||
self.partners_list.setStyleSheet("""
|
||||
QListWidget {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
outline: none;
|
||||
}
|
||||
QListWidget::item {
|
||||
border: none;
|
||||
padding: 0px;
|
||||
}
|
||||
QListWidget::item:selected {
|
||||
background-color: transparent;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(self.partners_list)
|
||||
|
||||
# Кнопка расчета материалов
|
||||
self.calc_button = QPushButton("Калькулятор материалов")
|
||||
self.calc_button.clicked.connect(self.show_material_calculator)
|
||||
self.calc_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #17a2b8;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #138496;
|
||||
}
|
||||
""")
|
||||
layout.addWidget(self.calc_button)
|
||||
|
||||
panel.setLayout(layout)
|
||||
return panel
|
||||
|
||||
def create_details_panel(self):
|
||||
"""Создание панели детальной информации"""
|
||||
panel = QWidget()
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
layout.setSpacing(10)
|
||||
|
||||
# Заголовок детальной информации
|
||||
self.details_title = QLabel("Выберите партнера")
|
||||
self.details_title.setFont(QFont("Arial", 14, QFont.Weight.Bold))
|
||||
self.details_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.details_title.setStyleSheet("padding: 10px;")
|
||||
layout.addWidget(self.details_title)
|
||||
|
||||
# Детальная информация о партнере - создаем пустой frame
|
||||
self.details_frame = QFrame()
|
||||
self.details_frame.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
self.details_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
""")
|
||||
self.details_layout = QVBoxLayout()
|
||||
self.details_layout.setSpacing(8)
|
||||
self.details_frame.setLayout(self.details_layout)
|
||||
self.details_frame.hide()
|
||||
|
||||
layout.addWidget(self.details_frame)
|
||||
|
||||
# Кнопки управления выбранным партнером
|
||||
self.control_buttons = QWidget()
|
||||
buttons_layout = QHBoxLayout()
|
||||
buttons_layout.setSpacing(10)
|
||||
|
||||
self.edit_button = QPushButton("Редактировать")
|
||||
self.edit_button.clicked.connect(self.edit_partner)
|
||||
self.edit_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #007acc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
self.edit_button.hide()
|
||||
|
||||
self.sales_button = QPushButton("История продаж")
|
||||
self.sales_button.clicked.connect(self.show_sales_history)
|
||||
self.sales_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
""")
|
||||
self.sales_button.hide()
|
||||
|
||||
self.discount_button = QPushButton("Расчет скидки")
|
||||
self.discount_button.clicked.connect(self.calculate_discount)
|
||||
self.discount_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #ffc107;
|
||||
color: black;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #e0a800;
|
||||
}
|
||||
""")
|
||||
self.discount_button.hide()
|
||||
|
||||
buttons_layout.addWidget(self.edit_button)
|
||||
buttons_layout.addWidget(self.sales_button)
|
||||
buttons_layout.addWidget(self.discount_button)
|
||||
buttons_layout.addStretch()
|
||||
|
||||
self.control_buttons.setLayout(buttons_layout)
|
||||
layout.addWidget(self.control_buttons)
|
||||
|
||||
# Добавляем растягивающийся элемент в конец
|
||||
layout.addStretch()
|
||||
|
||||
panel.setLayout(layout)
|
||||
return panel
|
||||
|
||||
def load_partners(self):
|
||||
"""Загрузка списка партнеров из API с авторизацией"""
|
||||
try:
|
||||
response = requests.get(
|
||||
"http://localhost:8000/api/v1/partners",
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
self.partners_list.clear()
|
||||
partners = response.json()
|
||||
|
||||
for partner in partners:
|
||||
item = QListWidgetItem()
|
||||
card = PartnerCard(partner)
|
||||
card.partner_clicked.connect(self.show_partner_details)
|
||||
|
||||
# Устанавливаем фиксированный размер для элемента
|
||||
item.setSizeHint(card.sizeHint())
|
||||
self.partners_list.addItem(item)
|
||||
self.partners_list.setItemWidget(item, card)
|
||||
|
||||
# Сбрасываем выделение
|
||||
self.partners_list.clearSelection()
|
||||
self.current_partner = None
|
||||
self.details_title.setText("Выберите партнера")
|
||||
self.details_frame.hide()
|
||||
self.edit_button.hide()
|
||||
self.sales_button.hide()
|
||||
self.discount_button.hide()
|
||||
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла. Пожалуйста, войдите снова.")
|
||||
self.logout()
|
||||
else:
|
||||
QMessageBox.warning(self, "Ошибка", "Не удалось загрузить партнеров")
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
QMessageBox.critical(self, "Ошибка", "Не удалось подключиться к серверу")
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось загрузить партнеров: {str(e)}")
|
||||
|
||||
def show_partner_details(self, partner_data):
|
||||
"""Отображение детальной информации о партнере"""
|
||||
self.current_partner = partner_data
|
||||
self.details_title.setText(partner_data['company_name'])
|
||||
|
||||
# Создаем новый виджет для деталей вместо очистки layout
|
||||
new_details_frame = QFrame()
|
||||
new_details_frame.setFrameStyle(QFrame.Shape.StyledPanel)
|
||||
new_details_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
}
|
||||
""")
|
||||
new_details_layout = QVBoxLayout()
|
||||
new_details_layout.setSpacing(8)
|
||||
|
||||
# Добавляем новую информацию
|
||||
details = [
|
||||
("Тип:", partner_data.get('partner_type', 'Не указан')),
|
||||
("ИНН:", partner_data.get('inn', 'Не указан')),
|
||||
("Директор:", partner_data.get('director_name', 'Не указан')),
|
||||
("Телефон:", partner_data.get('phone', 'Не указан')),
|
||||
("Email:", partner_data.get('email', 'Не указан')),
|
||||
("Рейтинг:", str(partner_data.get('rating', 0))),
|
||||
("Адрес:", partner_data.get('legal_address', 'Не указан')),
|
||||
("Регионы:", partner_data.get('sales_locations', 'Не указан'))
|
||||
]
|
||||
|
||||
for label, value in details:
|
||||
row_widget = QWidget()
|
||||
row_layout = QHBoxLayout(row_widget)
|
||||
row_layout.setContentsMargins(0, 2, 0, 2)
|
||||
|
||||
label_widget = QLabel(label)
|
||||
label_widget.setStyleSheet("font-weight: bold; min-width: 100px;")
|
||||
label_widget.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
value_widget = QLabel(str(value))
|
||||
value_widget.setWordWrap(True)
|
||||
value_widget.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
row_layout.addWidget(label_widget)
|
||||
row_layout.addWidget(value_widget)
|
||||
row_layout.addStretch()
|
||||
|
||||
new_details_layout.addWidget(row_widget)
|
||||
|
||||
new_details_frame.setLayout(new_details_layout)
|
||||
|
||||
# Заменяем старый details_frame на новый
|
||||
old_frame = self.details_frame
|
||||
layout = self.right_panel.layout()
|
||||
layout.replaceWidget(old_frame, new_details_frame)
|
||||
old_frame.deleteLater()
|
||||
|
||||
self.details_frame = new_details_frame
|
||||
self.details_layout = new_details_layout
|
||||
|
||||
self.details_frame.show()
|
||||
self.edit_button.show()
|
||||
self.sales_button.show()
|
||||
self.discount_button.show()
|
||||
|
||||
def show_add_partner_form(self):
|
||||
"""Открытие формы добавления партнера"""
|
||||
form = PartnerForm(self, auth=self.auth)
|
||||
form.partner_saved.connect(self.load_partners)
|
||||
form.exec()
|
||||
|
||||
def edit_partner(self):
|
||||
"""Редактирование выбранного партнера"""
|
||||
if self.current_partner:
|
||||
form = PartnerForm(self, self.current_partner, auth=self.auth)
|
||||
form.partner_saved.connect(self.load_partners)
|
||||
form.exec()
|
||||
|
||||
def show_sales_history(self):
|
||||
"""Открытие истории продаж партнера"""
|
||||
if self.current_partner:
|
||||
sales_window = SalesHistoryWindow(self.current_partner, self, auth=self.auth)
|
||||
sales_window.exec()
|
||||
|
||||
def calculate_discount(self):
|
||||
"""Расчет скидки для партнера с авторизацией"""
|
||||
if self.current_partner:
|
||||
try:
|
||||
response = requests.get(
|
||||
f"http://localhost:8000/api/v1/partners/{self.current_partner['partner_id']}/discount",
|
||||
auth=self.auth,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
discount_data = response.json()
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"Расчет скидки",
|
||||
f"Партнер: {self.current_partner['company_name']}\n"
|
||||
f"Общие продажи: {discount_data['total_sales']}\n"
|
||||
f"Скидка: {discount_data['discount_percent']}%"
|
||||
)
|
||||
elif response.status_code == 401:
|
||||
QMessageBox.warning(self, "Ошибка авторизации", "Сессия истекла. Пожалуйста, войдите снова.")
|
||||
self.logout()
|
||||
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self, "Ошибка", f"Не удалось рассчитать скидку: {str(e)}")
|
||||
|
||||
def show_material_calculator(self):
|
||||
"""Открытие калькулятора материалов"""
|
||||
calculator = MaterialCalculatorWindow(self, auth=self.auth)
|
||||
calculator.exec()
|
||||
|
||||
def logout(self):
|
||||
"""Выход из системы"""
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
"Подтверждение выхода",
|
||||
"Вы уверены, что хотите выйти из системы?",
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||
QMessageBox.StandardButton.No
|
||||
)
|
||||
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
self.close()
|
||||
# Здесь можно добавить вызов окна авторизации
|
||||
# или перезапуск приложения
|
||||
|
||||
def show_about(self):
|
||||
"""Показать информацию о программе"""
|
||||
QMessageBox.about(
|
||||
self,
|
||||
"О программе MasterPol",
|
||||
"MasterPol - Система управления партнерами\n\n"
|
||||
"Версия: 1.0.0\n"
|
||||
"Разработчик: Команда MasterPol\n\n"
|
||||
"Система предназначена для управления партнерами,\n"
|
||||
"учета продаж и расчета бизнес-показателей."
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue