Add Dispatch_V0.1.1

This commit is contained in:
2026-04-29 08:18:54 +04:00
commit a7ede6ded4
404 changed files with 39167 additions and 0 deletions

View File

@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
# hub/ticket/ui/pages/document_browser_page.py
"""Общая страница просмотра документов Ticket."""
from __future__ import annotations
from gui.components import Button, Label, TextInput
from gui.containers import HContainer, SContainer, VContainer
from application import TaskApplicationService
from domain import TicketDocumentSnapshot
from ui.ticket_selection_list import TicketSelectionEntry, TicketSelectionList
from .report_viewer import ReportViewer
class DocumentBrowserPage(SContainer):
"""Базовая страница списка документов и предпросмотра."""
def __init__(
self,
application: TaskApplicationService,
title: str,
empty_message: str,
document_type: str,
parent=None,
):
super().__init__(width_percent=100, height_percent=100, parent=parent)
self._application = application
self._title = title
self._empty_message = empty_message
self._document_type = document_type
self._documents: dict[str, TicketDocumentSnapshot] = {}
self._list_widget: TicketSelectionList | None = None
self._preview: TextInput | None = None
self._open_button: Button | None = None
self._setup_ui()
self._connect_signals()
self._reload_documents()
def _setup_ui(self) -> None:
# Root-контейнер страницы документов: заголовок плюс двухпанельная область.
main_container = VContainer(margin=12, spacing=10, parent=self)
title_label = Label(self._title, alignment="left", style="TICKET_LIST_HEADER")
# Content-row документов: раскладывает список документов и их предпросмотр.
content_row = HContainer(spacing=10, parent=main_container)
# Левая панель списка документов
list_panel = SContainer(style="TICKET_LIST_CONTAINER")
# List-layout: внутренний контейнер заголовка и списка документов.
list_layout = VContainer(margin=12, spacing=8, parent=list_panel)
list_title_label = Label("Список документов",
margin=[0, 0, 0, 8],
alignment="left",
style="TICKET_LIST_TITLE")
self._list_widget = TicketSelectionList()
list_layout.add_widget(list_title_label)
list_layout.add_widget(self._list_widget)
# Правая панель предпросмотра
preview_panel = SContainer(style="TICKET_LIST_CONTAINER")
# Preview-layout: внутренний контейнер заголовка, preview-поля и кнопки открытия.
preview_layout = VContainer(margin=12, spacing=8, parent=preview_panel)
preview_title_label = Label("Предпросмотр", alignment="left", style="TICKET_LIST_TITLE")
self._preview = TextInput(style="TICKET_PREVIEW_AREA", multiline=True)
self._preview.set_read_only(True)
self._open_button = Button("Открыть документ", style="FILTER_BUTTON", content_fit=True)
self._open_button.set_enabled(False)
preview_layout.add_widget(preview_title_label)
preview_layout.add_widget(self._preview)
preview_layout.add_widget(self._open_button)
content_row.add_widget_with_stretch(list_panel, 4)
content_row.add_widget_with_stretch(preview_panel, 7)
main_container.add_widget(title_label)
main_container.add_widget(content_row)
def _connect_signals(self) -> None:
# Application-состояние
self._application.task_updated.connect(self._reload_documents)
self._application.state_loaded.connect(self._reload_documents)
# UI-события страницы
if self._list_widget is not None:
self._list_widget.selection_changed.connect(self._update_preview)
self._list_widget.item_activated.connect(self._open_current_document)
if self._open_button is not None:
self._open_button.clicked.connect(self._open_current_document)
def _reload_documents(self, *_args) -> None:
if self._list_widget is None or self._preview is None:
return
current_document_id = self._current_document_id()
self._documents = {
document.document_id: document
for document in self._application.list_documents(self._document_type)
}
entries = [
TicketSelectionEntry(
entry_id=document.document_id,
title=document.title,
subtitle=document.created_at.strftime("%d.%m.%Y %H:%M"),
)
for document in self._documents.values()
]
self._list_widget.set_entries(entries)
if not self._documents:
self._preview.set_text(self._empty_message)
if self._open_button is not None:
self._open_button.set_enabled(False)
return
self._restore_selection(current_document_id)
self._update_preview()
def _restore_selection(self, document_id: str | None) -> None:
if self._list_widget is None:
return
self._list_widget.set_current_entry(document_id)
def _update_preview(self, *_args) -> None:
if self._preview is None:
return
document = self._current_document()
if document is None:
self._preview.set_text(self._empty_message)
if self._open_button is not None:
self._open_button.set_enabled(False)
return
self._preview.set_text(document.content or document.summary or document.title)
if self._open_button is not None:
self._open_button.set_enabled(True)
def _current_document(self) -> TicketDocumentSnapshot | None:
document_id = self._current_document_id()
if document_id is None:
return None
return self._documents.get(document_id)
def _current_document_id(self) -> str | None:
if self._list_widget is None:
return None
entry_id = self._list_widget.current_entry_id()
return str(entry_id) if entry_id is not None else None
def _open_current_document(self, *_args) -> None:
document = self._current_document()
if document is not None:
ReportViewer(document, parent=self).exec()
def showEvent(self, event) -> None:
super().showEvent(event)
self._reload_documents()