This commit is contained in:
2026-05-05 00:16:23 +08:00
parent fca1ea21d2
commit b0854509fe
9 changed files with 936 additions and 649 deletions
+309 -384
View File
@@ -35,6 +35,12 @@ ApplicationWindow {
property string statisticsPreset: "最近30天"
property int editingTimeSlotIndex: -1
property string pendingDeleteFaceId: ""
property int pendingStudentIdChangeIndex: -1
property string pendingStudentIdChangeNewId: ""
property string pendingStudentIdChangeName: ""
property string pendingStudentIdChangeClassDisplay: ""
property string editingFaceId: ""
property string editingFaceOriginalStudentId: ""
property string toastMessage: ""
property bool toastVisible: false
property bool resizingWindow: false
@@ -516,6 +522,20 @@ ApplicationWindow {
markMappingsDirty();
}
function requestStudentProfileCommit(index, studentId, studentName, classDisplay) {
var impactMessage = appController.studentIdChangeImpactMessage(index, studentId);
if (impactMessage.length > 0) {
pendingStudentIdChangeIndex = index;
pendingStudentIdChangeNewId = studentId;
pendingStudentIdChangeName = studentName;
pendingStudentIdChangeClassDisplay = classDisplay;
studentIdChangeDialog.text = impactMessage;
studentIdChangeDialog.open();
return;
}
commitStudentProfile(index, studentId, studentName, classDisplay);
}
function studentProfileMatches(searchText, classFilter, bindingFilter, studentId, studentName, classId, className, faceIds) {
var normalizedSearch = searchText ? searchText.trim().toLowerCase() : "";
var classFilterId = appController.classIdFromDisplay(classFilter);
@@ -535,6 +555,39 @@ ApplicationWindow {
return haystack.indexOf(normalizedSearch) >= 0;
}
function openFaceEditor(faceId, studentId, studentName, classId) {
editingFaceId = faceId;
editingFaceOriginalStudentId = studentId;
faceEditStudentIdField.text = studentId;
faceEditStudentNameField.text = studentName;
faceEditClassCombo.currentIndex = classComboIndex(classId);
faceEditDialog.open();
}
function saveFaceEditorNow() {
var savedLocally = appController.editFaceProfile(editingFaceId,
faceEditStudentIdField.text,
faceEditStudentNameField.text,
appController.classIdFromDisplay(faceEditClassCombo.currentText));
if (!savedLocally) {
faceEditDialog.open();
return;
}
markMappingsDirty();
appController.saveFaceMappings();
}
function requestSaveFaceEditor() {
var oldId = editingFaceOriginalStudentId ? editingFaceOriginalStudentId.trim() : "";
var newId = faceEditStudentIdField.text ? faceEditStudentIdField.text.trim() : "";
if (oldId.length > 0 && oldId !== newId) {
faceStudentIdChangeDialog.text = "修改该人脸绑定会影响识别后发送给 STM32 的学号、考勤记录显示、班级筛选和导出结果。确定继续吗?";
faceStudentIdChangeDialog.open();
return;
}
saveFaceEditorNow();
}
Component.onCompleted: {
applyRecordFilters();
applyStatisticsPreset();
@@ -548,7 +601,8 @@ ApplicationWindow {
return;
}
if (appController.statusMessage.indexOf("映射配置已保存") >= 0
|| appController.statusMessage.indexOf("映射配置已同步") >= 0) {
|| appController.statusMessage.indexOf("映射配置已同步") >= 0
|| appController.statusMessage.indexOf("人脸档案已保存") >= 0) {
mappingsDirty = false;
}
toastMessage = appController.statusMessage;
@@ -572,7 +626,7 @@ ApplicationWindow {
id: recordExportDialog
fileMode: FileDialog.SaveFile
nameFilters: ["CSV 文件 (*.csv)"]
currentFile: "attendance_export.csv"
defaultSuffix: "csv"
onAccepted: appController.exportFilteredRecords(selectedFile)
}
@@ -580,7 +634,7 @@ ApplicationWindow {
id: statisticsExportDialog
fileMode: FileDialog.SaveFile
nameFilters: ["CSV 文件 (*.csv)"]
currentFile: "attendance_statistics.csv"
defaultSuffix: "csv"
onAccepted: appController.exportStatistics(selectedFile)
}
@@ -784,7 +838,7 @@ ApplicationWindow {
spacing: 12
Label {
text: "确定删除 ID 为 " + pendingDeleteFaceId + " 的人脸信息吗?"
text: "确定删除人脸编号 " + pendingDeleteFaceId + " 吗?"
wrapMode: Text.WordWrap
font.pixelSize: 18
font.weight: appBoldWeight
@@ -801,6 +855,116 @@ ApplicationWindow {
onAccepted: appController.deleteFace(pendingDeleteFaceId, deleteFaceRecordsCheck.checked)
}
Dialog {
id: faceEditDialog
modal: true
anchors.centerIn: Overlay.overlay
width: phoneLayout ? Math.min(window.width - 32, 420) : 460
title: "编辑人脸档案"
standardButtons: Dialog.Save | Dialog.Cancel
background: Rectangle {
radius: 20
color: panelStrong
border.color: lineColor
}
enter: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 0
to: 1
duration: 150
}
NumberAnimation {
property: "scale"
from: 0.96
to: 1.0
duration: 180
easing.type: Easing.OutCubic
}
}
}
exit: Transition {
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 1
to: 0
duration: 120
}
NumberAnimation {
property: "scale"
from: 1.0
to: 0.98
duration: 120
}
}
}
contentItem: ColumnLayout {
spacing: 12
Label {
Layout.fillWidth: true
text: "人脸编号: " + editingFaceId
color: ink900
font.pixelSize: 18
font.weight: appBoldWeight
elide: Text.ElideRight
}
AppTextField {
id: faceEditStudentIdField
Layout.fillWidth: true
placeholderText: "真实学号"
}
AppTextField {
id: faceEditStudentNameField
Layout.fillWidth: true
placeholderText: "姓名"
}
AppComboBox {
id: faceEditClassCombo
Layout.fillWidth: true
model: appController.classSelectionOptions
}
Label {
Layout.fillWidth: true
text: "人脸编号由 K210 生成,不要改成学号;这里只编辑学号、姓名和班级。留空学号可解除绑定。"
wrapMode: Text.WordWrap
color: ink500
font.pixelSize: 12
}
}
onAccepted: requestSaveFaceEditor()
}
MessageDialog {
id: faceStudentIdChangeDialog
title: "确认修改绑定学号"
buttons: MessageDialog.Yes | MessageDialog.No
onAccepted: saveFaceEditorNow()
}
MessageDialog {
id: studentIdChangeDialog
title: "确认修改学号"
buttons: MessageDialog.Yes | MessageDialog.No
onAccepted: {
commitStudentProfile(pendingStudentIdChangeIndex,
pendingStudentIdChangeNewId,
pendingStudentIdChangeName,
pendingStudentIdChangeClassDisplay);
}
}
Rectangle {
anchors.fill: parent
anchors.margins: slimLayout ? 8 : 16
@@ -2353,8 +2517,8 @@ ApplicationWindow {
SectionHeader {
Layout.fillWidth: true
title: "名称映射配置"
description: "让学号和班级编号显示为更清晰的真实名称。"
title: "班级管理"
description: "新建、命名和删除班级;学生归属在人脸管理的编辑窗口中维护。"
accent: tealColor
}
@@ -2367,398 +2531,155 @@ ApplicationWindow {
}
GridLayout {
Layout.fillWidth: true
columns: 2
columns: phoneLayout ? 1 : 4
columnSpacing: 8
rowSpacing: 8
SecondaryButton {
Layout.fillWidth: true
visible: mappingsExpanded
text: phoneLayout ? "加载" : "从服务器加载"
text: phoneLayout ? "加载" : "从服务器加载班级"
onClicked: appController.loadMappingsFromServer()
}
PrimaryButton {
Layout.fillWidth: true
visible: mappingsExpanded
text: "保存映射"
text: "保存班级"
fillColor: tealColor
onClicked: appController.saveMappings()
}
SecondaryButton {
Layout.fillWidth: phoneLayout
Layout.preferredWidth: phoneLayout ? -1 : 120
visible: mappingsExpanded
text: "新增班级"
onClicked: {
appController.classMappingModel.addEntry("", "");
markMappingsDirty();
}
}
Label {
Layout.fillWidth: phoneLayout
Layout.alignment: Qt.AlignVCenter
visible: mappingsExpanded
text: mappingsDirty ? "有未保存改动" : "配置已保存"
color: mappingsDirty ? goldColor : successColor
font.pixelSize: 12
font.weight: appBoldWeight
}
}
}
TabBar {
id: mappingTabs
ListView {
id: classMappingListView
visible: mappingsExpanded
Layout.fillWidth: true
Layout.preferredHeight: visible ? 42 : 0
Layout.preferredHeight: visible ? Math.max(phoneLayout ? 112 : 64, contentHeight) : 0
clip: false
interactive: false
spacing: 8
padding: 0
background: Rectangle {
color: "transparent"
}
model: appController.classMappingModel
SegmentTabButton {
width: (mappingTabs.width - mappingTabs.spacing) / 2
text: "学生档案"
}
SegmentTabButton {
width: (mappingTabs.width - mappingTabs.spacing) / 2
text: "班级配置"
}
}
delegate: Rectangle {
width: ListView.view.width
height: phoneLayout ? 112 : 56
property bool hovered: classMappingHoverHandler.hovered
radius: 8
color: hovered ? "#eef5f4" : (index % 2 === 0 ? "#ffffff" : "#f8f3ec")
border.color: hovered ? "#c7dddd" : (index % 2 === 0 ? "#ece4da" : "#e7ded3")
StackLayout {
visible: mappingsExpanded
Layout.fillWidth: true
Layout.preferredHeight: visible ? Math.max(phoneLayout ? 168 : 74, mappingTabs.currentIndex === 0 ? studentProfileListView.contentHeight : classMappingListView.contentHeight) : 0
currentIndex: mappingTabs.currentIndex
Behavior on color {
enabled: !resizingWindow
ColorAnimation { duration: 120 }
}
Behavior on border.color {
enabled: !resizingWindow
ColorAnimation { duration: 120 }
}
Rectangle {
color: "transparent"
HoverHandler {
id: classMappingHoverHandler
}
ListView {
id: studentProfileListView
RowLayout {
anchors.fill: parent
clip: false
interactive: false
spacing: 8
model: appController.studentProfileModel
anchors.leftMargin: 12
anchors.rightMargin: 12
visible: !phoneLayout
spacing: 12
header: ColumnLayout {
width: ListView.view.width
spacing: 8
GridLayout {
Layout.fillWidth: true
columns: phoneLayout ? 1 : 5
columnSpacing: 8
rowSpacing: 8
AppTextField {
Layout.fillWidth: true
placeholderText: "搜索学号、姓名、班级或人脸 ID"
text: appController.studentProfileSearchText
onTextEdited: appController.studentProfileSearchText = text
}
AppComboBox {
Layout.fillWidth: true
model: appController.studentProfileClassFilterOptions
currentIndex: optionIndex(model, appController.studentProfileClassFilter)
onActivated: appController.studentProfileClassFilter = currentText
}
AppComboBox {
Layout.fillWidth: true
model: ["全部", "已绑定", "未绑定"]
currentIndex: optionIndex(model, appController.studentProfileBindingFilter)
onActivated: appController.studentProfileBindingFilter = currentText
}
SecondaryButton {
Layout.fillWidth: true
text: "新增学生"
onClicked: {
appController.addStudentProfile();
markMappingsDirty();
}
}
Label {
Layout.alignment: Qt.AlignVCenter
text: mappingsDirty ? "有未保存改动" : "配置已保存"
color: mappingsDirty ? goldColor : successColor
font.pixelSize: 12
font.weight: appBoldWeight
}
AppTextField {
id: classKeyField
Layout.preferredWidth: 220
text: model.entryKey
placeholderText: "班级编号"
onEditingFinished: {
appController.classMappingModel.updateEntry(index, text, classValueField.text);
markMappingsDirty();
}
}
delegate: Rectangle {
id: studentProfileRow
width: ListView.view.width
height: visible ? (phoneLayout ? 168 : 64) : 0
visible: studentProfileMatches(appController.studentProfileSearchText,
appController.studentProfileClassFilter,
appController.studentProfileBindingFilter,
model.studentId,
model.studentName,
model.classId,
model.className,
model.faceIds)
property int rowIndex: index
property string rowClassId: model.classId
property bool hovered: studentProfileHoverHandler.hovered
radius: 8
color: hovered ? "#eef5f4" : (index % 2 === 0 ? "#ffffff" : "#f8f3ec")
border.color: hovered ? "#c7dddd" : (index % 2 === 0 ? "#ece4da" : "#e7ded3")
Behavior on color {
enabled: !resizingWindow
ColorAnimation {
duration: 120
}
}
Behavior on border.color {
enabled: !resizingWindow
ColorAnimation {
duration: 120
}
AppTextField {
id: classValueField
Layout.preferredWidth: 260
text: model.entryValue
placeholderText: "班级名称"
onEditingFinished: {
appController.classMappingModel.updateEntry(index, classKeyField.text, text);
markMappingsDirty();
}
}
HoverHandler {
id: studentProfileHoverHandler
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 12
anchors.rightMargin: 12
visible: !phoneLayout
spacing: 10
AppTextField {
id: profileStudentIdField
Layout.preferredWidth: 160
text: model.studentId
placeholderText: "学号"
onEditingFinished: commitStudentProfile(studentProfileRow.rowIndex, text, profileStudentNameField.text, profileClassCombo.currentText)
}
AppTextField {
id: profileStudentNameField
Layout.preferredWidth: 150
text: model.studentName
placeholderText: "姓名"
onEditingFinished: commitStudentProfile(studentProfileRow.rowIndex, profileStudentIdField.text, text, profileClassCombo.currentText)
}
AppComboBox {
id: profileClassCombo
Layout.preferredWidth: 220
model: appController.classSelectionOptions
Component.onCompleted: currentIndex = classComboIndex(studentProfileRow.rowClassId)
onModelChanged: currentIndex = classComboIndex(studentProfileRow.rowClassId)
onActivated: commitStudentProfile(studentProfileRow.rowIndex, profileStudentIdField.text, profileStudentNameField.text, currentText)
}
Label {
Layout.preferredWidth: 160
text: model.faceIds.length > 0 ? model.faceIds : "未绑定人脸"
color: model.faceIds.length > 0 ? successColor : ink500
font.pixelSize: 12
elide: Text.ElideRight
}
Item {
Layout.fillWidth: true
}
SecondaryButton {
text: "删除"
onClicked: {
appController.removeStudentProfile(studentProfileRow.rowIndex);
markMappingsDirty();
}
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 12
visible: phoneLayout
spacing: 8
RowLayout {
Layout.fillWidth: true
spacing: 8
AppTextField {
id: profileStudentIdFieldPhone
Layout.fillWidth: true
text: model.studentId
placeholderText: "学号"
onEditingFinished: commitStudentProfile(studentProfileRow.rowIndex, text, profileStudentNameFieldPhone.text, profileClassComboPhone.currentText)
}
AppTextField {
id: profileStudentNameFieldPhone
Layout.fillWidth: true
text: model.studentName
placeholderText: "姓名"
onEditingFinished: commitStudentProfile(studentProfileRow.rowIndex, profileStudentIdFieldPhone.text, text, profileClassComboPhone.currentText)
}
}
RowLayout {
Layout.fillWidth: true
spacing: 8
AppComboBox {
id: profileClassComboPhone
Layout.fillWidth: true
model: appController.classSelectionOptions
Component.onCompleted: currentIndex = classComboIndex(studentProfileRow.rowClassId)
onModelChanged: currentIndex = classComboIndex(studentProfileRow.rowClassId)
onActivated: commitStudentProfile(studentProfileRow.rowIndex, profileStudentIdFieldPhone.text, profileStudentNameFieldPhone.text, currentText)
}
SecondaryButton {
Layout.preferredWidth: 76
text: "删除"
onClicked: {
appController.removeStudentProfile(studentProfileRow.rowIndex);
markMappingsDirty();
}
}
}
Label {
Layout.fillWidth: true
text: model.faceIds.length > 0 ? "绑定人脸: " + model.faceIds : "未绑定人脸"
color: model.faceIds.length > 0 ? successColor : ink500
font.pixelSize: 12
elide: Text.ElideRight
}
Item {
Layout.fillWidth: true
}
SecondaryButton {
text: "删除"
onClicked: {
if (appController.removeClassMapping(index))
markMappingsDirty();
}
}
}
}
Rectangle {
color: "transparent"
ListView {
id: classMappingListView
ColumnLayout {
anchors.fill: parent
clip: false
interactive: false
anchors.margins: 12
visible: phoneLayout
spacing: 8
model: appController.classMappingModel
header: RowLayout {
width: ListView.view.width
spacing: 12
AppTextField {
id: classKeyFieldPhone
Layout.fillWidth: true
text: model.entryKey
placeholderText: "班级编号"
onEditingFinished: {
appController.classMappingModel.updateEntry(index, text, classValueFieldPhone.text);
markMappingsDirty();
}
}
SecondaryButton {
text: "新增班级"
onClicked: {
appController.classMappingModel.addEntry("", "");
RowLayout {
Layout.fillWidth: true
spacing: 8
AppTextField {
id: classValueFieldPhone
Layout.fillWidth: true
text: model.entryValue
placeholderText: "班级名称"
onEditingFinished: {
appController.classMappingModel.updateEntry(index, classKeyFieldPhone.text, text);
markMappingsDirty();
}
}
Item {
Layout.fillWidth: true
}
}
delegate: Rectangle {
width: ListView.view.width
height: phoneLayout ? 112 : 56
property bool hovered: classMappingHoverHandler.hovered
radius: 8
color: hovered ? "#eef5f4" : (index % 2 === 0 ? "#ffffff" : "#f8f3ec")
border.color: hovered ? "#c7dddd" : (index % 2 === 0 ? "#ece4da" : "#e7ded3")
Behavior on color {
enabled: !resizingWindow
ColorAnimation {
duration: 120
}
}
Behavior on border.color {
enabled: !resizingWindow
ColorAnimation {
duration: 120
}
}
HoverHandler {
id: classMappingHoverHandler
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 12
anchors.rightMargin: 12
visible: !phoneLayout
spacing: 12
AppTextField {
id: classKeyField
Layout.preferredWidth: 220
text: model.entryKey
placeholderText: "班级编号"
onEditingFinished: {
appController.classMappingModel.updateEntry(index, text, classValueField.text);
SecondaryButton {
Layout.preferredWidth: 76
text: "删除"
onClicked: {
if (appController.removeClassMapping(index))
markMappingsDirty();
}
}
AppTextField {
id: classValueField
Layout.preferredWidth: 260
text: model.entryValue
placeholderText: "班级名称"
onEditingFinished: {
appController.classMappingModel.updateEntry(index, classKeyField.text, text);
markMappingsDirty();
}
}
Item {
Layout.fillWidth: true
}
SecondaryButton {
text: "删除"
onClicked: {
appController.classMappingModel.removeEntry(index);
markMappingsDirty();
}
}
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 12
visible: phoneLayout
spacing: 8
AppTextField {
id: classKeyFieldPhone
Layout.fillWidth: true
text: model.entryKey
placeholderText: "班级编号"
onEditingFinished: {
appController.classMappingModel.updateEntry(index, text, classValueFieldPhone.text);
markMappingsDirty();
}
}
RowLayout {
Layout.fillWidth: true
spacing: 8
AppTextField {
id: classValueFieldPhone
Layout.fillWidth: true
text: model.entryValue
placeholderText: "班级名称"
onEditingFinished: {
appController.classMappingModel.updateEntry(index, classKeyFieldPhone.text, text);
markMappingsDirty();
}
}
SecondaryButton {
Layout.preferredWidth: 76
text: "删除"
onClicked: {
appController.classMappingModel.removeEntry(index);
markMappingsDirty();
}
}
}
}
}
@@ -2817,8 +2738,8 @@ ApplicationWindow {
SectionHeader {
Layout.fillWidth: true
title: "K210 人脸照片与学号映射"
description: "同步照片状态,维护人脸与真实学号的对应关系。"
title: "K210 人脸照片与档案绑定"
description: "同步照片状态,维护人脸、学号、姓名和班级信息。"
accent: goldColor
}
@@ -2832,7 +2753,7 @@ ApplicationWindow {
onClicked: appController.refreshFaces()
}
PrimaryButton {
text: "保存映射"
text: "保存档案"
onClicked: appController.saveFaceMappings()
}
}
@@ -2853,12 +2774,12 @@ ApplicationWindow {
clip: true
property int cardColumns: phoneLayout ? 1 : Math.max(1, Math.floor(width / (compactLayout ? 236 : 260)))
cellWidth: Math.max(phoneLayout ? 280 : 240, Math.floor(width / cardColumns))
cellHeight: phoneLayout ? 462 : 424
cellHeight: phoneLayout ? 444 : 404
model: appController.faceModel
delegate: Rectangle {
width: Math.min(phoneLayout ? 300 : 244, GridView.view.cellWidth - 18)
height: phoneLayout ? 438 : 400
height: phoneLayout ? 420 : 380
property bool hovered: faceHoverHandler.hovered
radius: 8
color: hovered ? "#fff6ef" : "#ffffff"
@@ -2912,23 +2833,25 @@ ApplicationWindow {
}
Label {
text: "ID: " + model.faceId
text: "人脸编号: " + model.faceId
color: ink900
font.pixelSize: compactLayout ? 15 : 16
font.weight: appBoldWeight
}
AppTextField {
Layout.fillWidth: true
text: model.studentId
placeholderText: narrowLayout ? "学号" : "输入真实学号"
onEditingFinished: appController.updateFaceStudentId(index, text)
}
ColumnLayout {
Layout.fillWidth: true
spacing: 4
Label {
Layout.fillWidth: true
text: model.studentId.length > 0 ? "学号: " + model.studentId : "学号: 未绑定"
color: model.studentId.length > 0 ? ink700 : goldColor
font.pixelSize: 12
font.weight: model.studentId.length > 0 ? appBoldWeight : Font.Normal
elide: Text.ElideRight
}
Label {
Layout.fillWidth: true
text: model.studentName.length > 0 ? "姓名: " + model.studentName : "姓名: 未建档"
@@ -2950,12 +2873,12 @@ ApplicationWindow {
Layout.fillWidth: true
Layout.preferredHeight: 34
radius: 8
color: model.studentId.length === 0 ? "#f9ebcf" : (model.studentName.length > 0 ? "#e6f4ec" : "#fce8e5")
color: model.studentId.length === 0 ? "#f9ebcf" : (model.studentName.length > 0 ? "#e6f4ec" : "#f9ebcf")
Label {
anchors.centerIn: parent
text: model.studentId.length === 0 ? "未绑定学生" : (model.studentName.length > 0 ? "已绑定学生档案" : "学号未建档")
color: model.studentId.length === 0 ? goldColor : (model.studentName.length > 0 ? successColor : dangerColor)
text: model.studentId.length === 0 ? "未绑定学生" : (model.studentName.length > 0 ? "已绑定学生档案" : "待补全学生信息")
color: model.studentId.length === 0 ? goldColor : (model.studentName.length > 0 ? successColor : goldColor)
font.pixelSize: narrowLayout ? 11 : 12
font.weight: appBoldWeight
}
@@ -2980,13 +2903,23 @@ ApplicationWindow {
Layout.fillHeight: true
}
DangerButton {
RowLayout {
Layout.fillWidth: true
text: "删除该人脸"
onClicked: {
pendingDeleteFaceId = model.faceId;
deleteFaceRecordsCheck.checked = false;
deleteFaceDialog.open();
SecondaryButton {
Layout.fillWidth: true
text: "编辑"
onClicked: openFaceEditor(model.faceId, model.studentId, model.studentName, model.classId)
}
DangerButton {
Layout.fillWidth: true
text: "删除"
onClicked: {
pendingDeleteFaceId = model.faceId;
deleteFaceRecordsCheck.checked = false;
deleteFaceDialog.open();
}
}
}
}
@@ -3040,12 +2973,10 @@ ApplicationWindow {
anchors.margins: phoneLayout ? 16 : 24
clip: true
contentWidth: availableWidth
contentHeight: aboutContentLayout.height
ColumnLayout {
id: aboutContentLayout
width: aboutScrollView.availableWidth
height: Math.max(aboutScrollView.availableHeight, implicitHeight)
spacing: phoneLayout ? 12 : 18
SectionHeader {
@@ -3079,12 +3010,6 @@ ApplicationWindow {
lineHeight: 1.25
}
Item {
Layout.fillHeight: true
Layout.minimumHeight: 0
Layout.preferredHeight: 0
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: thesisInfoTitle.implicitHeight + thesisInfoBody.paintedHeight + thesisInfoLayout.spacing + 32