/
evernotenkai
/
DataBaseXJavaProject
Обзор
Документация
Войти
/
evernotenkai
/
DataBaseXJavaProject
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/app/ui/student/StudentSubmissionCenterView.java
389 строк
19 KB
Makson4ik
Весь проект с src db и прочим делом
12 май 2026, 22:43
12 май 2026, 22:43
431f4aa
Код
Авторство
О чём код?
package app.ui.student; import java.awt.Desktop; import java.io.File; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import app.model.AssignmentItem; import app.model.CourseItem; import app.model.SubmissionItem; import app.service.LmsService; import app.ui.components.PageHeader; import app.ui.components.UiKit; import app.util.Dialogs; import app.util.HtmlUtil; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.SplitPane; import javafx.scene.input.MouseButton; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.scene.web.HTMLEditor; import javafx.stage.FileChooser; import javafx.stage.Window; public class StudentSubmissionCenterView extends VBox { private final LmsService service; private final int studentId; private final Runnable onChanged; private final ObservableList<SubmissionItem> items = FXCollections.observableArrayList(); private final ObservableList<AssignmentItem> assignments = FXCollections.observableArrayList(); private final ObservableList<CourseItem> courses = FXCollections.observableArrayList(); private final ComboBox<CourseItem> courseBox = new ComboBox<CourseItem>(); private final ComboBox<AssignmentItem> assignmentBox = new ComboBox<AssignmentItem>(); private final TextField searchField = new TextField(); private final HTMLEditor editor = new HTMLEditor(); private final TableView<SubmissionItem> history = new TableView<SubmissionItem>(); private final Label attachmentLabel = UiKit.labeled("Файл не прикреплён", "detail-value"); private final Label assignmentsHint = UiKit.labeled("", "detail-line"); private final Label emptyState = UiKit.labeled("", "muted-text"); private File selectedAttachmentFile; private byte[] selectedAttachmentBytes; private String selectedAttachmentMime; private String currentAttachmentName; public StudentSubmissionCenterView(final LmsService service, final int studentId) { this(service, studentId, null); } public StudentSubmissionCenterView(final LmsService service, final int studentId, Runnable onChanged) { this.service = service; this.studentId = studentId; this.onChanged = onChanged; getStyleClass().add("content-root"); setSpacing(16); getChildren().add(new PageHeader("Сдача работ", "Выбирай курс, быстро находи нужное задание, прикладывай файл и отправляй обновлённую версию без дублей.")); courseBox.setPromptText("Курс-фильтр"); courseBox.setPrefWidth(280); assignmentBox.setPromptText("Выберите задание"); assignmentBox.setPrefWidth(380); assignmentBox.setCellFactory(v -> new javafx.scene.control.ListCell<AssignmentItem>() { @Override protected void updateItem(AssignmentItem item, boolean empty) { super.updateItem(item, empty); setText(empty || item == null ? "" : item.getCourseName() + " — " + item.getTitle()); } }); assignmentBox.setButtonCell(new javafx.scene.control.ListCell<AssignmentItem>() { @Override protected void updateItem(AssignmentItem item, boolean empty) { super.updateItem(item, empty); setText(empty || item == null ? "" : item.getCourseName() + " — " + item.getTitle()); } }); searchField.setPromptText("Поиск по названию задания"); searchField.setPrefWidth(280); editor.setHtmlText(HtmlUtil.wrapHtml("")); editor.getStyleClass().add("rich-editor"); editor.setPrefHeight(380); editor.setMinHeight(280); VBox.setVgrow(editor, Priority.ALWAYS); initHistoryTable(); Button save = UiKit.primaryButton("Отправить / обновить"); Button clear = UiKit.ghostButton("Очистить"); Button attach = UiKit.secondaryButton("Прикрепить файл"); Button removeAttach = UiKit.ghostButton("Снять файл"); Button openAttach = UiKit.ghostButton("Открыть файл"); HBox filters = new HBox(10, courseBox, searchField); HBox.setHgrow(courseBox, Priority.ALWAYS); HBox.setHgrow(searchField, Priority.ALWAYS); filters.getStyleClass().add("toolbar"); emptyState.getStyleClass().add("empty-state"); emptyState.setWrapText(true); emptyState.setVisible(false); emptyState.setManaged(false); VBox left = UiKit.card( UiKit.section("Форма отправки"), UiKit.subtitle("Система показывает только задания доступных тебе курсов. Если список пуст, администратор ещё не зачислил тебя или преподаватель не опубликовал задания."), filters, UiKit.fieldBox("Задание", assignmentBox, "Выбирай задание из списка или воспользуйся фильтром по курсу"), assignmentsHint, emptyState, UiKit.fieldBox("Текст ответа", editor, "Редактор поддерживает форматирование. Можно добавить файл и краткое пояснение."), UiKit.fieldBox("Вложение", attachmentLabel, "Прикладывай архив, документ, скриншот или любой другой файл"), new HBox(10, save, attach, openAttach, removeAttach, clear) ); VBox right = UiKit.card( UiKit.section("История отправок"), UiKit.subtitle("Здесь видно все версии работы, статус и оценку преподавателя."), history ); SplitPane split = new SplitPane(left, right); split.setDividerPositions(0.48); split.widthProperty().addListener((obs, oldW, newW) -> split.setDividerPositions(newW.doubleValue() < 1240 ? 0.54 : 0.48)); split.getStyleClass().add("workspace-split-pane"); HBox.setHgrow(split, Priority.ALWAYS); getChildren().add(split); courseBox.valueProperty().addListener((obs, oldV, newV) -> refreshAssignments()); searchField.textProperty().addListener((obs, oldV, newV) -> refreshAssignments()); assignmentBox.valueProperty().addListener((obs, oldV, newV) -> loadSelection(newV)); history.getSelectionModel().selectedItemProperty().addListener((obs, old, sel) -> { if (sel == null) { return; } AssignmentItem ass = null; for (AssignmentItem a : assignments) { if (a.getId() == sel.getAssignmentId()) { ass = a; break; } } if (ass != null && (assignmentBox.getValue() == null || ass.getId() != assignmentBox.getValue().getId())) { assignmentBox.getSelectionModel().select(ass); } editor.setHtmlText(HtmlUtil.wrapHtml(sel.getContentNote())); currentAttachmentName = sel.getAttachmentName(); attachmentLabel.setText(currentAttachmentName == null ? "Файл не прикреплён" : currentAttachmentName); }); save.setOnAction(e -> { try { AssignmentItem ass = assignmentBox.getValue(); if (ass == null) { Dialogs.info("Сдача", "Сначала выбери задание."); return; } String html = editor.getHtmlText(); boolean blankText = HtmlUtil.isBlankHtml(html); boolean hasAttachment = selectedAttachmentBytes != null || currentAttachmentName != null; if (blankText && !hasAttachment) { Dialogs.info("Сдача", "Напиши текст ответа или прикрепи файл."); return; } service.submitWork(studentId, ass.getId(), html, selectedAttachmentFile == null ? null : selectedAttachmentFile.getName(), selectedAttachmentFile == null ? null : selectedAttachmentMime, selectedAttachmentBytes); refresh(); notifyChanged(); Dialogs.info("Сдача", "Работа сохранена."); } catch (Exception ex) { Dialogs.error("Ошибка отправки", ex); } }); attach.setOnAction(e -> chooseAttachment()); openAttach.setOnAction(e -> openCurrentAttachment()); removeAttach.setOnAction(e -> clearAttachment()); clear.setOnAction(e -> { history.getSelectionModel().clearSelection(); editor.setHtmlText(HtmlUtil.wrapHtml("")); assignmentBox.getSelectionModel().clearSelection(); clearAttachment(); selectedAttachmentFile = null; selectedAttachmentBytes = null; selectedAttachmentMime = null; }); refresh(); } private void initHistoryTable() { UiKit.configureTable(history); TableColumn<SubmissionItem, String> c1 = new TableColumn<SubmissionItem, String>("Курс"); c1.setCellValueFactory(v -> new SimpleStringProperty(v.getValue().getCourseName())); TableColumn<SubmissionItem, String> c2 = new TableColumn<SubmissionItem, String>("Задание"); c2.setCellValueFactory(v -> new SimpleStringProperty(v.getValue().getAssignmentTitle())); TableColumn<SubmissionItem, String> c3 = new TableColumn<SubmissionItem, String>("Дата"); c3.setCellValueFactory(v -> new SimpleStringProperty(v.getValue().getSubmissionDate())); TableColumn<SubmissionItem, String> c4 = new TableColumn<SubmissionItem, String>("Статус"); c4.setCellValueFactory(v -> new SimpleStringProperty(v.getValue().getStatus())); c4.setCellFactory(col -> new TableCell<SubmissionItem, String>() { protected void updateItem(String item, boolean empty) { super.updateItem(item, empty); setText(empty || item == null ? "" : item); setGraphic(empty || item == null ? null : UiKit.chip(item, UiKit.statusClass(item))); } }); TableColumn<SubmissionItem, String> c5 = new TableColumn<SubmissionItem, String>("Оценка"); c5.setCellValueFactory(v -> new SimpleStringProperty(v.getValue().getGradeValue() == null ? "" : String.valueOf(v.getValue().getGradeValue()))); TableColumn<SubmissionItem, String> c6 = new TableColumn<SubmissionItem, String>("Вложение"); c6.setCellValueFactory(v -> new SimpleStringProperty(v.getValue().getAttachmentName() == null ? "" : v.getValue().getAttachmentName())); c6.setCellFactory(col -> new TableCell<SubmissionItem, String>() { protected void updateItem(String item, boolean empty) { super.updateItem(item, empty); setText(null); setGraphic(empty || item == null || item.trim().isEmpty() ? null : UiKit.chip(item, "file-chip")); } }); TableColumn<SubmissionItem, String> c7 = new TableColumn<SubmissionItem, String>("Комментарий"); c7.setCellValueFactory(v -> new SimpleStringProperty(v.getValue().getTeacherComment() == null ? "" : v.getValue().getTeacherComment())); history.getColumns().addAll(c1, c2, c3, c4, c5, c6, c7); history.setItems(items); VBox.setVgrow(history, Priority.ALWAYS); } private void chooseAttachment() { try { Window window = getScene() == null ? null : getScene().getWindow(); FileChooser chooser = new FileChooser(); chooser.setTitle("Выбери файл для вложения"); File file = chooser.showOpenDialog(window); if (file == null) { return; } selectedAttachmentFile = file; selectedAttachmentBytes = Files.readAllBytes(file.toPath()); selectedAttachmentMime = Files.probeContentType(file.toPath()); attachmentLabel.setText("Новый файл: " + file.getName()); } catch (Exception ex) { Dialogs.error("Файл", ex); } } private void clearAttachment() { selectedAttachmentFile = null; selectedAttachmentBytes = null; selectedAttachmentMime = null; attachmentLabel.setText(currentAttachmentName == null ? "Файл не прикреплён" : currentAttachmentName); } private void openCurrentAttachment() { try { AssignmentItem ass = assignmentBox.getValue(); if (ass == null) { Dialogs.info("Файл", "Сначала выбери задание."); return; } SubmissionItem sub = service.findStudentSubmissionByAssignment(studentId, ass.getId()); if (sub == null || sub.getAttachmentName() == null) { Dialogs.info("Файл", "У этой отправки нет вложения."); return; } app.model.SubmissionAttachmentData attachment = service.findSubmissionAttachment(sub.getId()); if (attachment == null || !attachment.hasData()) { Dialogs.info("Файл", "У этой отправки нет вложения."); return; } File temp = File.createTempFile("mini-lms-", "_" + sub.getAttachmentName()); temp.deleteOnExit(); Files.write(temp.toPath(), attachment.getData()); if (Desktop.isDesktopSupported()) { Desktop.getDesktop().open(temp); } } catch (Exception ex) { Dialogs.error("Файл", ex); } } private void loadSelection(AssignmentItem assignment) { editor.setHtmlText(HtmlUtil.wrapHtml("")); currentAttachmentName = null; clearAttachment(); if (assignment == null) { return; } SubmissionItem sub = service.findStudentSubmissionByAssignment(studentId, assignment.getId()); if (sub != null) { editor.setHtmlText(HtmlUtil.wrapHtml(sub.getContentNote())); currentAttachmentName = sub.getAttachmentName(); attachmentLabel.setText(currentAttachmentName == null ? "Файл не прикреплён" : currentAttachmentName); } else { attachmentLabel.setText("Файл не прикреплён"); } selectedAttachmentFile = null; selectedAttachmentBytes = null; selectedAttachmentMime = null; } public void refresh() { courses.setAll(service.findStudentCourses(studentId)); List<CourseItem> courseOptions = new ArrayList<CourseItem>(); courseOptions.add(new CourseItem(-1, "Все мои курсы", "Показывает задания по всем доступным курсам", "")); courseOptions.addAll(courses); CourseItem currentCourse = courseBox.getValue(); courseBox.getItems().setAll(courseOptions); if (currentCourse != null) { for (CourseItem c : courseOptions) { if (c.getId() == currentCourse.getId()) { courseBox.getSelectionModel().select(c); break; } } } if (courseBox.getValue() == null && !courseOptions.isEmpty()) { courseBox.getSelectionModel().select(0); } refreshAssignments(); history.getItems().setAll(service.findStudentSubmissions(studentId)); } private void refreshAssignments() { CourseItem course = courseBox.getValue(); String query = searchField.getText() == null ? "" : searchField.getText().trim().toLowerCase(); List<AssignmentItem> source; if (course == null || course.getId() < 0) { source = service.findStudentAssignments(studentId); } else { source = service.findAssignmentsForCourse(course.getId()); } assignments.clear(); for (AssignmentItem item : source) { if (query.isEmpty() || containsIgnoreCase(item.getTitle(), query) || containsIgnoreCase(item.getCourseName(), query)) { assignments.add(item); } } AssignmentItem current = assignmentBox.getValue(); assignmentBox.getItems().setAll(assignments); if (current != null) { for (AssignmentItem a : assignments) { if (a.getId() == current.getId()) { assignmentBox.getSelectionModel().select(a); break; } } } if (assignmentBox.getValue() == null && !assignments.isEmpty()) { assignmentBox.getSelectionModel().select(0); } assignmentsHint.setText(assignments.isEmpty() ? "По выбранному фильтру заданий не найдено. Проверь зачисление на курс или попробуй другой фильтр." : "Доступных заданий: " + assignments.size()); emptyState.setVisible(assignments.isEmpty()); emptyState.setManaged(assignments.isEmpty()); emptyState.setText(assignments.isEmpty() ? "Сейчас здесь пусто. Если курс уже выбран, значит преподаватель ещё не опубликовал задания или ты не зачислен на курс." : ""); } private boolean containsIgnoreCase(String source, String query) { return source != null && source.toLowerCase().contains(query); } private void notifyChanged() { if (onChanged != null) { onChanged.run(); } } }