2025年4月17日16:56:58

master
qmstyle 2025-04-17 16:57:08 +08:00
parent 9ad557eb28
commit 36e31ee2d8
6 changed files with 557 additions and 4 deletions

View File

@ -3,6 +3,8 @@ package com.zhangmeng.online.exam.ui.api.model;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.zhangmeng.online.exam.ui.components.SendPaperComponent;
import com.zhangmeng.online.exam.ui.components.callBack.SendPaperComponentCallBackImpl;
import com.zhangmeng.online.exam.ui.utils.ApiUtils; import com.zhangmeng.online.exam.ui.utils.ApiUtils;
import com.zhangmeng.online.exam.ui.api.DataLoad; import com.zhangmeng.online.exam.ui.api.DataLoad;
import com.zhangmeng.online.exam.ui.components.PaperViewComponent; import com.zhangmeng.online.exam.ui.components.PaperViewComponent;
@ -118,8 +120,39 @@ public class PaperDataLoad implements DataLoad {
@Override @Override
public List<MenuItem> getContextMenu() { public List<MenuItem> getContextMenu() {
//组卷
MenuItem group_volumes = group_volumes();
//发卷
MenuItem send_paper = send_paper();
return Arrays.asList(group_volumes,send_paper);
}
//发卷
private MenuItem send_paper(){
MenuItem group_volumes = new MenuItem("发卷");
group_volumes.setOnAction(event -> {
Map<String, SimpleStringProperty> map = ApiUtils.selectedItem(group_volumes);
String id = map.get("id").getValue();
ContextMenu parentPopup = group_volumes.getParentPopup();
BorderPane indexPage = (BorderPane) parentPopup.getStyleableParent();
Stage stage = (Stage) indexPage.getScene().getWindow();
Map<String, Object> context = new HashMap<>();
context.put("id", id);
SendPaperComponent paperViewComponent = new SendPaperComponent(new SendPaperComponentCallBackImpl(),context);
paperViewComponent.setPrefWidth(1200);
paperViewComponent.setPrefHeight(649);
AlertUtils.alert("组卷", paperViewComponent, 1200, 649, stage, true);
});
return group_volumes;
}
private MenuItem group_volumes(){
MenuItem group_volumes = new MenuItem("组卷"); MenuItem group_volumes = new MenuItem("组卷");
group_volumes.setOnAction(event -> { group_volumes.setOnAction(event -> {
Map<String, SimpleStringProperty> map = ApiUtils.selectedItem(group_volumes); Map<String, SimpleStringProperty> map = ApiUtils.selectedItem(group_volumes);
@ -139,10 +172,10 @@ public class PaperDataLoad implements DataLoad {
AlertUtils.alert("组卷", paperViewComponent, 1200, 649, stage, true); AlertUtils.alert("组卷", paperViewComponent, 1200, 649, stage, true);
}); });
return group_volumes;
return Arrays.asList(group_volumes);
} }
@Override @Override
public ApiUtils.DataView searchData(Map<String, Object> searchText, int pageNum, int pageSize) { public ApiUtils.DataView searchData(Map<String, Object> searchText, int pageNum, int pageSize) {
AlertUtils.alert_msg("搜索功能正在开发中..." + JSON.toJSONString(searchText)); AlertUtils.alert_msg("搜索功能正在开发中..." + JSON.toJSONString(searchText));

View File

@ -0,0 +1,460 @@
package com.zhangmeng.online.exam.ui.components;
import com.zhangmeng.online.exam.ui.components.callBack.PaperViewComponentCallBack;
import com.zhangmeng.online.exam.ui.components.callBack.SendPaperComponentCallBack;
import com.zhangmeng.online.exam.ui.components.callBack.SendPaperComponentCallBackImpl;
import com.zhangmeng.online.exam.ui.utils.AlertUtils;
import com.zhangmeng.online.exam.ui.utils.ApiUtils;
import javafx.application.Platform;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.control.cell.MapValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Paint;
import javafx.util.Callback;
import org.casic.javafx.control.PaginationPicker;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*
* @author zm
* @date 2025/3/19 12:22
* @version: 1.0
*/
public class SendPaperComponent extends SplitPane {
private static final double Default_Width = 20;
//附加信息
private Map<String, Object> context = new HashMap<>();
private VBox vbox;
private VBox chooseVbox;
private SendPaperComponentCallBack callBack;
private TableView<Map<String, SimpleStringProperty>> questionTable;
private final ObservableList<Map<String, SimpleStringProperty>> questionTable_data = FXCollections.observableArrayList();
private TableColumn<Map<String, SimpleStringProperty>, String> chooseColumn;
private TableView<Map<String, SimpleStringProperty>> chooseTable;
//已选题目列表
private final ObservableList<Map<String, SimpleStringProperty>> chooseTable_data = FXCollections.observableArrayList();
private SimpleIntegerProperty SINGLE_CHOICE_SCORE = new SimpleIntegerProperty();//单选题分数
private SimpleIntegerProperty MULTIPLE_CHOICE_SCORE = new SimpleIntegerProperty();//多选题分数
private SimpleIntegerProperty JUDGMENT_SCORE = new SimpleIntegerProperty();//判断题分数
private SimpleIntegerProperty NUMERICAL_SCORE = new SimpleIntegerProperty();//计算题分数
private SimpleIntegerProperty SHORT_ANSWER_SCORE = new SimpleIntegerProperty();//简答题分数
private SimpleIntegerProperty Fill_IN_THE_BLANKS = new SimpleIntegerProperty();//填空题分数
private SimpleIntegerProperty ANSWER_SCORE = new SimpleIntegerProperty();//答题总分
private PaperResultHeaderComponent hBox;
public SendPaperComponent(SendPaperComponentCallBack callBack) {
super();
setCallBack(callBack);
initQuestionTable();
initChooseTable();
this.setOrientation(Orientation.VERTICAL);
this.setDividerPositions(0.5);
this.getItems().add(vbox);
this.getItems().add(chooseVbox);
}
public SendPaperComponent(SendPaperComponentCallBack callBack, Map<String, Object> context) {
super();
setContext(context);
setCallBack(callBack);
initQuestionTable();
initChooseTable();
this.setOrientation(Orientation.VERTICAL);
this.setDividerPositions(0.5);
this.getItems().add(vbox);
this.getItems().add(chooseVbox);
}
private void initQuestionTable() {
vbox = new VBox(5);
vbox.setAlignment(Pos.CENTER_LEFT);
vbox.prefHeightProperty().bind(this.heightProperty().divide(2));
vbox.prefWidthProperty().bind(this.widthProperty());
ApiUtils.DataView dataView = callBack.loadPaperData(ApiUtils.PAGE_NUM, ApiUtils.PAGE_SIZE);
questionTable = new TableView<>();
questionTable.setItems(questionTable_data);
// questionTable.setEditable(true);
questionTable.prefHeightProperty().bind(vbox.heightProperty());
questionTable.prefWidthProperty().bind(vbox.widthProperty());
List<String> columns = dataView.getKeys().stream().filter(key -> !key.equals("id")).toList();
columns.forEach(col -> {
TableColumn<Map<String, SimpleStringProperty>, String> column = new TableColumn<>(col);
// 数据绑定‌:ml-citation{ref="1,3" data="citationList"}
column.setCellValueFactory(new MapValueFactory(col));
// 启用单元格编辑‌:ml-citation{ref="4,5" data="citationList"}
column.setCellFactory(TextFieldTableCell.forTableColumn());
column.prefWidthProperty().bind(questionTable.widthProperty().divide(columns.size()).subtract(Default_Width));
questionTable.getColumns().add(column);
});
//添加按钮
TableColumn<Map<String, SimpleStringProperty>, String> addColumn = new TableColumn<>("操作");
addColumn.setCellFactory(new Callback<>() {
@Override
public TableCell<Map<String, SimpleStringProperty>, String> call(TableColumn<Map<String, SimpleStringProperty>, String> mapStringTableColumn) {
return new TableCell<>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
HBox hBox = new HBox();
// hBox.setPrefHeight(30);
hBox.setAlignment(Pos.CENTER);
// hBox.setStyle("-fx-background-color: #1094e9");
Button button = new Button("添加");
button.setTextFill(Paint.valueOf("#1094e9"));
TableRow<Map<String, SimpleStringProperty>> row = this.getTableRow();
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
if (row.getItem() != null) {
Platform.runLater(new Runnable() {
@Override
public void run() {
chooseTable_data.add(row.getItem());
questionTable_data.remove(row.getItem());
}
});
}
}
});
hBox.getChildren().addAll(button);
this.setGraphic(hBox);
} else {
this.setGraphic(null);
}
}
};
}
});
addColumn.prefWidthProperty().bind(questionTable.widthProperty().divide(columns.size() + 2).subtract(50));
questionTable.getColumns().add(addColumn);
questionTable_data.addAll(dataView.getDataList());
//初始化一个分页
PaginationPicker paginationPicker = new PaginationPicker();
paginationPicker.setTotal(dataView.getTotal());//设置总数据量默认0
paginationPicker.setPageSize(dataView.getPageSize());//设置每页显示条数默认30
paginationPicker.setPageButtonCount(9);//设置页码按钮的数量默认7当总页数超过该值时会折叠大于等于 5 且小于等于 21 的奇数
paginationPicker.setCurrentPage(dataView.getPageNum());//设置当前选择页码默认第一页注意必须放在所有设置条件之后。不小于0 并且 不大于总页数
paginationPicker.setPaginationButtonFontSize(12);//设置分页字体大小默认10(不小于2)
//监听点击动作事件
paginationPicker.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
// System.out.println("当前选择页码:"+paginationPicker.getCurrentPage());
questionTable_data.clear();
questionTable_data.addAll(callBack.loadPaperData(paginationPicker.getCurrentPage(), paginationPicker.getPageSize()).getDataList());
}
});
PaperSearchComponent hBox = new PaperSearchComponent(5);
hBox.getSearchButton().setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
String searchText = hBox.getQuestionName().get();
if (searchText == null || searchText.trim().isEmpty()) {
AlertUtils.alert_msg("搜索内容不能为空!");
return;
}
questionTable_data.clear();
questionTable_data.addAll(callBack.searchPaperData(searchText, paginationPicker.getCurrentPage(), paginationPicker.getPageSize()).getDataList());
}
});
vbox.setPadding(new Insets(5));
vbox.getChildren().addAll(hBox, questionTable, paginationPicker);
}
private void initChooseTable() {
chooseVbox = new VBox(5);
chooseVbox.setStyle("-fx-border-width: 1px; -fx-border-color: #e53030; -fx-border-style: solid;");
chooseVbox.setAlignment(Pos.CENTER_LEFT);
chooseVbox.prefHeightProperty().bind(this.heightProperty().divide(2));
chooseVbox.prefWidthProperty().bind(this.widthProperty());
ApiUtils.DataView dataView = callBack.loadChoosePaperData(context, ApiUtils.PAGE_NUM, ApiUtils.PAGE_SIZE);
chooseTable = new TableView<>();
chooseTable.setItems(chooseTable_data);
// chooseTable.setEditable(true);
chooseTable.prefHeightProperty().bind(chooseVbox.heightProperty());
chooseTable.prefWidthProperty().bind(chooseVbox.widthProperty());
List<String> columns = dataView.getKeys().stream().filter(key -> !key.equals("id")).toList();
columns.forEach(col -> {
TableColumn<Map<String, SimpleStringProperty>, String> column = new TableColumn<>(col);
// 数据绑定‌:ml-citation{ref="1,3" data="citationList"}
column.setCellValueFactory(new MapValueFactory(col));
// 启用单元格编辑‌:ml-citation{ref="4,5" data="citationList"}
column.setCellFactory(TextFieldTableCell.forTableColumn());
column.prefWidthProperty().bind(chooseTable.widthProperty().divide(columns.size()).subtract(Default_Width));
chooseTable.getColumns().add(column);
});
//添加按钮
TableColumn<Map<String, SimpleStringProperty>, String> addColumn = new TableColumn<>("操作");
addColumn.setCellFactory(new Callback<>() {
@Override
public TableCell<Map<String, SimpleStringProperty>, String> call(TableColumn<Map<String, SimpleStringProperty>, String> mapStringTableColumn) {
return new TableCell<>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
HBox hBox = new HBox();
// hBox.setPrefHeight(30);
hBox.setAlignment(Pos.CENTER);
// hBox.setStyle("-fx-background-color: #1094e9");
Button button = new Button("删除");
button.setTextFill(Paint.valueOf("#1094e9"));
TableRow<Map<String, SimpleStringProperty>> row = this.getTableRow();
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
if (row.getItem() != null) {
Platform.runLater(new Runnable() {
@Override
public void run() {
questionTable_data.add(row.getItem());
chooseTable_data.remove(row.getItem());
}
});
}
}
});
hBox.getChildren().addAll(button);
this.setGraphic(hBox);
} else {
this.setGraphic(null);
}
}
};
}
});
addColumn.prefWidthProperty().bind(chooseTable.widthProperty().divide(columns.size() + 2).subtract(50));
chooseTable.getColumns().add(addColumn);
chooseTable_data.addAll(dataView.getDataList());
//初始化一个分页
PaginationPicker paginationPicker = new PaginationPicker();
paginationPicker.setTotal(dataView.getTotal());//设置总数据量默认0
paginationPicker.setPageSize(dataView.getPageSize());//设置每页显示条数默认30
paginationPicker.setPageButtonCount(9);//设置页码按钮的数量默认7当总页数超过该值时会折叠大于等于 5 且小于等于 21 的奇数
paginationPicker.setCurrentPage(dataView.getPageNum());//设置当前选择页码默认第一页注意必须放在所有设置条件之后。不小于0 并且 不大于总页数
paginationPicker.setPaginationButtonFontSize(12);//设置分页字体大小默认10(不小于2)
//监听点击动作事件
paginationPicker.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
// System.out.println("当前选择页码:"+paginationPicker.getCurrentPage());
chooseTable_data.clear();
chooseTable_data.addAll(callBack.loadPaperData(paginationPicker.getCurrentPage(), paginationPicker.getPageSize()).getDataList());
}
});
hBox = new PaperResultHeaderComponent(15);
hBox.getCommitBtn().setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
if (chooseTable_data.size() <= 0) {
AlertUtils.alert_msg("请先选择试题!");
return;
}
callBack.commitChoosePaper(context, chooseTable_data);
chooseTable_data.clear();
questionTable_data.clear();
new Thread(() -> {
chooseTable_data.addAll(callBack.loadChoosePaperData(context, ApiUtils.PAGE_NUM, ApiUtils.PAGE_SIZE).getDataList());
questionTable_data.addAll(callBack.loadPaperData(ApiUtils.PAGE_NUM, ApiUtils.PAGE_SIZE).getDataList());
}).start();
}
});
chooseVbox.setPadding(new Insets(5));
chooseVbox.getChildren().addAll(hBox, chooseTable, paginationPicker);
chooseTable_data.addListener(new ListChangeListener<>() {
@Override
public void onChanged(Change<? extends Map<String, SimpleStringProperty>> change) {
//计算分值
scoreFlush((ObservableList<Map<String, SimpleStringProperty>>) change.getList());
}
});
initScore();
}
private void scoreFlush(ObservableList<Map<String, SimpleStringProperty>> changeList) {
//初始化分值
SINGLE_CHOICE_SCORE.set(0);
MULTIPLE_CHOICE_SCORE.set(0);
JUDGMENT_SCORE.set(0);
NUMERICAL_SCORE.set(0);
SHORT_ANSWER_SCORE.set(0);
Fill_IN_THE_BLANKS.set(0);
reloadData(changeList);
}
private void reloadData(List<Map<String, SimpleStringProperty>> changeList) {
for (Map<String, SimpleStringProperty> chooseTableDatum : changeList) {
int score = 0;
if (chooseTableDatum.get("score").get() != null){
score = Integer.parseInt(chooseTableDatum.get("score").get());
}
SimpleStringProperty type = chooseTableDatum.get("type");
switch (type.get()) {
case "单选题" -> SINGLE_CHOICE_SCORE.set(SINGLE_CHOICE_SCORE.get() + score);
case "多选题" -> MULTIPLE_CHOICE_SCORE.set(MULTIPLE_CHOICE_SCORE.get() + score);
case "判断题" -> JUDGMENT_SCORE.set(JUDGMENT_SCORE.get() + score);
case "计算题" -> NUMERICAL_SCORE.set(NUMERICAL_SCORE.get() + score);
case "填空题" -> Fill_IN_THE_BLANKS.set(Fill_IN_THE_BLANKS.get() + score);
case "简答题" -> SHORT_ANSWER_SCORE.set(SHORT_ANSWER_SCORE.get() + score);
default -> throw new IllegalStateException("Unexpected value: " + type.get());
}
}
//计算总分
ANSWER_SCORE.set(
SINGLE_CHOICE_SCORE.get()
+ MULTIPLE_CHOICE_SCORE.get()
+ JUDGMENT_SCORE.get()
+ NUMERICAL_SCORE.get()
+ SHORT_ANSWER_SCORE.get()
+ Fill_IN_THE_BLANKS.get()
);
hBox.setSINGLE_CHOICE_SCORE("单选题分数:" + SINGLE_CHOICE_SCORE.get());
hBox.setMULTIPLE_CHOICE_SCORE("多选题分数:" + MULTIPLE_CHOICE_SCORE.get());
hBox.setJUDGMENT_SCORE("判断题分数:" + JUDGMENT_SCORE.get());
hBox.setNUMERICAL_SCORE("计算题分数:" + NUMERICAL_SCORE.get());
hBox.setSHORT_ANSWER_SCORE("简答题分数:" + SHORT_ANSWER_SCORE.get());
hBox.setFill_IN_THE_BLANKS("填空题分数:" + Fill_IN_THE_BLANKS.get());
hBox.setANSWER_SCORE("答题总分:" + ANSWER_SCORE.get());
}
private void initScore() {
reloadData(chooseTable_data);
}
public TableView<Map<String, SimpleStringProperty>> getQuestionTable() {
return questionTable;
}
public void setQuestionTable(TableView<Map<String, SimpleStringProperty>> questionTable) {
this.questionTable = questionTable;
}
public TableView<Map<String, SimpleStringProperty>> getChooseTable() {
return chooseTable;
}
public void setChooseTable(TableView<Map<String, SimpleStringProperty>> chooseTable) {
this.chooseTable = chooseTable;
}
public ObservableList<Map<String, SimpleStringProperty>> getQuestionTable_data() {
return questionTable_data;
}
public SendPaperComponentCallBack getCallBack() {
return callBack;
}
public void setCallBack(SendPaperComponentCallBack callBack) {
this.callBack = callBack;
}
public ObservableList<Map<String, SimpleStringProperty>> getChooseTable_data() {
return chooseTable_data;
}
public VBox getVbox() {
return vbox;
}
public void setVbox(VBox vbox) {
this.vbox = vbox;
}
public VBox getChooseVbox() {
return chooseVbox;
}
public void setChooseVbox(VBox chooseVbox) {
this.chooseVbox = chooseVbox;
}
public Map<String, Object> getContext() {
return context;
}
public void setContext(Map<String, Object> context) {
this.context = context;
}
}

View File

@ -0,0 +1,23 @@
package com.zhangmeng.online.exam.ui.components.callBack;
import com.zhangmeng.online.exam.ui.utils.ApiUtils;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.ObservableList;
import java.util.Map;
/**
* @author zm
* @date 2025/4/17 16:50
* @version: 1.0
*/
public interface SendPaperComponentCallBack {
ApiUtils.DataView loadPaperData(int pageNum, int pageSize);
ApiUtils.DataView searchPaperData(String searchText, int currentPage, int pageSize);
ApiUtils.DataView loadChoosePaperData(Map<String, Object> context, int pageNum, int pageSize);
void commitChoosePaper(Map<String, Object> context, ObservableList<Map<String, SimpleStringProperty>> chooseTableData);
}

View File

@ -0,0 +1,36 @@
package com.zhangmeng.online.exam.ui.components.callBack;
import com.zhangmeng.online.exam.ui.utils.ApiUtils;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.ObservableList;
import java.util.Map;
/**
* @author zm
* @date 2025/4/17 16:50
* @version: 1.0
*/
public class SendPaperComponentCallBackImpl implements SendPaperComponentCallBack {
@Override
public ApiUtils.DataView loadPaperData(int pageNum, int pageSize) {
return null;
}
@Override
public ApiUtils.DataView searchPaperData(String searchText, int currentPage, int pageSize) {
return null;
}
@Override
public ApiUtils.DataView loadChoosePaperData(Map<String, Object> context, int pageNum, int pageSize) {
return null;
}
@Override
public void commitChoosePaper(Map<String, Object> context, ObservableList<Map<String, SimpleStringProperty>> chooseTableData) {
}
}

View File

@ -124,7 +124,8 @@ public class LoginController {
if (newValue.intValue() == 3) { if (newValue.intValue() == 3) {
myService.cancel(); myService.cancel();
//alert.close(); //alert.close();
success(); //success();
admin_page();
} }
} }
} }

View File

@ -127,7 +127,7 @@ public class MainController {
case "/option/list" -> init_table(ApiUtils.getQuestionOptionList(),FxUtils.QuestionOption_DynamicTableComponent); case "/option/list" -> init_table(ApiUtils.getQuestionOptionList(),FxUtils.QuestionOption_DynamicTableComponent);
case "/permission/list" -> init_table(ApiUtils.getPermissionList(),FxUtils.Permission_DynamicTableComponent); case "/permission/list" -> init_table(ApiUtils.getPermissionList(),FxUtils.Permission_DynamicTableComponent);
case "/profession/list" -> init_table(ApiUtils.getProfessionList(),FxUtils.Profession_DynamicTableComponent); case "/profession/list" -> init_table(ApiUtils.getProfessionList(),FxUtils.Profession_DynamicTableComponent);
case "发布公告" -> FXMLLoader.load(getClass().getResource("/fxml/notice-edit.fxml")); case "/exam/list" -> FXMLLoader.load(getClass().getResource("/fxml/notice-edit.fxml"));
case "在线用户" -> FXMLLoader.load(getClass().getResource("/fxml/monitor-online.fxml")); case "在线用户" -> FXMLLoader.load(getClass().getResource("/fxml/monitor-online.fxml"));
case "系统日志" -> FXMLLoader.load(getClass().getResource("/fxml/monitor-log.fxml")); case "系统日志" -> FXMLLoader.load(getClass().getResource("/fxml/monitor-log.fxml"));
case "性能监控" -> FXMLLoader.load(getClass().getResource("/fxml/monitor-performance.fxml")); case "性能监控" -> FXMLLoader.load(getClass().getResource("/fxml/monitor-performance.fxml"));