diff --git a/.gitignore b/.gitignore index e741630..bcc1bca 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ build/ build-*/ cmake-build-*/ out/ +dist/ # CMake generated files CMakeCache.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 7684448..3b7a7ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,10 @@ project(rewrite_qt VERSION 1.0.0 LANGUAGES CXX) set(REWRITE_QT_DISPLAY_NAME "人脸识别考勤系统") set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml") +set(REWRITE_QT_OUTPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dist") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${REWRITE_QT_OUTPUT_DIR}") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${REWRITE_QT_OUTPUT_DIR}") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${REWRITE_QT_OUTPUT_DIR}") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) @@ -20,6 +24,15 @@ file(MAKE_DIRECTORY "${REWRITE_QT_AUTOGEN_DIR}") find_package(Qt6 6.2 REQUIRED COMPONENTS Core Gui Qml Quick QuickControls2 Network) +get_target_property(REWRITE_QT_QMAKE_EXECUTABLE Qt6::qmake IMPORTED_LOCATION) +if(REWRITE_QT_QMAKE_EXECUTABLE) + execute_process( + COMMAND "${REWRITE_QT_QMAKE_EXECUTABLE}" -query QT_INSTALL_QML + OUTPUT_VARIABLE REWRITE_QT_INSTALL_QML + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +endif() + qt_standard_project_setup() set(REWRITE_QT_APP_ICON "${CMAKE_CURRENT_SOURCE_DIR}/assets/favicon.ico") @@ -46,6 +59,12 @@ qt_add_executable(apprewrite_qt src/FaceListModel.cpp ) +if(REWRITE_QT_INSTALL_QML) + target_compile_definitions(apprewrite_qt PRIVATE + REWRITE_QT_INSTALL_QML="${REWRITE_QT_INSTALL_QML}" + ) +endif() + if(WIN32 AND EXISTS "${REWRITE_QT_APP_ICON}") set(REWRITE_QT_WIN32_ICON_RC "${CMAKE_CURRENT_BINARY_DIR}/app_icon.rc") configure_file(cmake/app_icon.rc.in "${REWRITE_QT_WIN32_ICON_RC}" @ONLY) diff --git a/main.cpp b/main.cpp index 9baa04f..101ba11 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -32,6 +34,16 @@ int main(int argc, char *argv[]) AppController controller; QQmlApplicationEngine engine; + const QString qmlImportPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); + if (!qmlImportPath.isEmpty()) { + engine.addImportPath(qmlImportPath); + } +#if defined(REWRITE_QT_INSTALL_QML) + const QString qtInstallQmlPath = QStringLiteral(REWRITE_QT_INSTALL_QML); + if (QDir(qtInstallQmlPath).exists()) { + engine.addImportPath(qtInstallQmlPath); + } +#endif engine.rootContext()->setContextProperty("appController", &controller); const QUrl url(QStringLiteral("qrc:/qml/Main.qml")); diff --git a/qml/Main.qml b/qml/Main.qml index 9fad271..54192a5 100644 --- a/qml/Main.qml +++ b/qml/Main.qml @@ -51,17 +51,17 @@ ApplicationWindow { font.family: appFontFamily onWidthChanged: { - resizingWindow = true - resizeSettledTimer.restart() + resizingWindow = true; + resizeSettledTimer.restart(); } onHeightChanged: { - resizingWindow = true - resizeSettledTimer.restart() + resizingWindow = true; + resizeSettledTimer.restart(); } function switchPage(index) { - currentPageIndex = Math.max(0, Math.min(4, index)) + currentPageIndex = Math.max(0, Math.min(4, index)); } Timer { @@ -165,9 +165,7 @@ ApplicationWindow { background: Rectangle { radius: 8 - color: control.down ? Qt.darker(control.fillColor, 1.12) - : control.hovered ? Qt.lighter(control.fillColor, 1.06) - : control.fillColor + color: control.down ? Qt.darker(control.fillColor, 1.12) : control.hovered ? Qt.lighter(control.fillColor, 1.06) : control.fillColor } contentItem: Label { @@ -191,9 +189,7 @@ ApplicationWindow { background: Rectangle { radius: 8 - color: control.down ? "#efe6db" - : control.hovered ? "#f7f1ea" - : control.fillColor + color: control.down ? "#efe6db" : control.hovered ? "#f7f1ea" : control.fillColor border.color: control.hovered ? accentSoft : control.strokeColor } @@ -434,74 +430,60 @@ ApplicationWindow { } function applyRecordFilters() { - appController.applyFilters( - classFilter.currentText, - studentFilter.currentText, - nameFilter.text, - dateFilter.currentIndex, - specificDateField.text) + appController.applyFilters(classFilter.currentText, studentFilter.currentText, nameFilter.text, dateFilter.currentIndex, specificDateField.text); } function applyStatisticsPreset() { - const today = new Date() - let start = new Date(today) - let end = new Date(today) + const today = new Date(); + let start = new Date(today); + let end = new Date(today); if (statisticsPreset === "今天") { - start = new Date(today) + start = new Date(today); } else if (statisticsPreset === "最近7天") { - start.setDate(start.getDate() - 7) + start.setDate(start.getDate() - 7); } else if (statisticsPreset === "最近30天") { - start.setDate(start.getDate() - 30) + start.setDate(start.getDate() - 30); } else if (statisticsPreset === "本月") { - start = new Date(today.getFullYear(), today.getMonth(), 1) + start = new Date(today.getFullYear(), today.getMonth(), 1); } - statsStartField.text = Qt.formatDate(start, "yyyy-MM-dd") - statsEndField.text = Qt.formatDate(end, "yyyy-MM-dd") - appController.updateStatisticsRange(statsStartField.text, statsEndField.text) + statsStartField.text = Qt.formatDate(start, "yyyy-MM-dd"); + statsEndField.text = Qt.formatDate(end, "yyyy-MM-dd"); + appController.updateStatisticsRange(statsStartField.text, statsEndField.text); } function openTimeSlotDialog(index) { - editingTimeSlotIndex = index + editingTimeSlotIndex = index; if (index >= 0) { - const item = appController.timeSlotModel.get(index) - timeSlotNameField.text = item.name - timeSlotStartField.text = item.startTime - timeSlotEndField.text = item.endTime - timeSlotEnabledBox.checked = item.enabled - timeSlotDialog.title = "编辑时间段" + const item = appController.timeSlotModel.get(index); + timeSlotNameField.text = item.name; + timeSlotStartField.text = item.startTime; + timeSlotEndField.text = item.endTime; + timeSlotEnabledBox.checked = item.enabled; + timeSlotDialog.title = "编辑时间段"; } else { - timeSlotNameField.text = "" - timeSlotStartField.text = "08:00" - timeSlotEndField.text = "09:30" - timeSlotEnabledBox.checked = true - timeSlotDialog.title = "新增时间段" + timeSlotNameField.text = ""; + timeSlotStartField.text = "08:00"; + timeSlotEndField.text = "09:30"; + timeSlotEnabledBox.checked = true; + timeSlotDialog.title = "新增时间段"; } - timeSlotDialog.open() + timeSlotDialog.open(); } function saveTimeSlotDialog() { if (editingTimeSlotIndex >= 0) { - appController.timeSlotModel.updateTimeSlot( - editingTimeSlotIndex, - timeSlotNameField.text, - timeSlotStartField.text, - timeSlotEndField.text, - timeSlotEnabledBox.checked) + appController.timeSlotModel.updateTimeSlot(editingTimeSlotIndex, timeSlotNameField.text, timeSlotStartField.text, timeSlotEndField.text, timeSlotEnabledBox.checked); } else { - appController.timeSlotModel.addTimeSlot( - timeSlotNameField.text, - timeSlotStartField.text, - timeSlotEndField.text, - timeSlotEnabledBox.checked) + appController.timeSlotModel.addTimeSlot(timeSlotNameField.text, timeSlotStartField.text, timeSlotEndField.text, timeSlotEnabledBox.checked); } - appController.saveTimeSlots() + appController.saveTimeSlots(); } Component.onCompleted: { - applyRecordFilters() - applyStatisticsPreset() + applyRecordFilters(); + applyStatisticsPreset(); } Connections { @@ -509,11 +491,11 @@ ApplicationWindow { function onStatusMessageChanged() { if (!appController.statusMessage || appController.statusMessage.length === 0) { - return + return; } - toastMessage = appController.statusMessage - toastVisible = true - toastTimer.restart() + toastMessage = appController.statusMessage; + toastVisible = true; + toastTimer.restart(); } } @@ -560,15 +542,36 @@ ApplicationWindow { 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 } + 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 } + NumberAnimation { + property: "opacity" + from: 1 + to: 0 + duration: 120 + } + NumberAnimation { + property: "scale" + from: 1.0 + to: 0.98 + duration: 120 + } } } @@ -607,15 +610,36 @@ ApplicationWindow { 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 } + 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 } + NumberAnimation { + property: "opacity" + from: 1 + to: 0 + duration: 120 + } + NumberAnimation { + property: "scale" + from: 1.0 + to: 0.98 + duration: 120 + } } } @@ -665,15 +689,36 @@ ApplicationWindow { 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 } + 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 } + NumberAnimation { + property: "opacity" + from: 1 + to: 0 + duration: 120 + } + NumberAnimation { + property: "scale" + from: 1.0 + to: 0.98 + duration: 120 + } } } @@ -747,11 +792,31 @@ ApplicationWindow { Repeater { model: [ - { title: "考勤记录", hint: "查看筛选与导出", icon: "01" }, - { title: "统计分析", hint: "按时间统计趋势", icon: "02" }, - { title: "配置中心", hint: "时间段与映射设置", icon: "03" }, - { title: "人脸管理", hint: "照片与学号绑定", icon: "04" }, - { title: "关于", hint: "系统说明", icon: "05" } + { + title: "考勤记录", + hint: "查看筛选与导出", + icon: "01" + }, + { + title: "统计分析", + hint: "按时间统计趋势", + icon: "02" + }, + { + title: "配置中心", + hint: "时间段与映射设置", + icon: "03" + }, + { + title: "人脸管理", + hint: "照片与学号绑定", + icon: "04" + }, + { + title: "关于", + hint: "系统说明", + icon: "05" + } ] delegate: Rectangle { @@ -762,16 +827,22 @@ ApplicationWindow { Layout.fillWidth: true Layout.preferredHeight: 74 radius: 8 - color: selected ? "#eef7f7" - : navMouse.containsMouse ? "#214957" - : "#173743" + color: selected ? "#eef7f7" : navMouse.containsMouse ? "#214957" : "#173743" border.width: 1 - border.color: selected ? "#9fd4d7" - : navMouse.containsMouse ? "#3b6470" - : "#254a55" + border.color: selected ? "#9fd4d7" : navMouse.containsMouse ? "#3b6470" : "#254a55" - Behavior on color { enabled: !resizingWindow; ColorAnimation { duration: 120 } } - Behavior on border.color { enabled: !resizingWindow; ColorAnimation { duration: 120 } } + Behavior on color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } + } + Behavior on border.color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } + } Rectangle { anchors.right: parent.right @@ -783,8 +854,18 @@ ApplicationWindow { color: tealColor opacity: navButton.selected ? 1 : 0 - Behavior on height { enabled: !resizingWindow; NumberAnimation { duration: 150 } } - Behavior on opacity { enabled: !resizingWindow; NumberAnimation { duration: 120 } } + Behavior on height { + enabled: !resizingWindow + NumberAnimation { + duration: 150 + } + } + Behavior on opacity { + enabled: !resizingWindow + NumberAnimation { + duration: 120 + } + } } RowLayout { @@ -807,29 +888,29 @@ ApplicationWindow { } } - ColumnLayout { + ColumnLayout { + Layout.fillWidth: true + spacing: 2 + + Label { Layout.fillWidth: true - spacing: 2 + text: modelData.title + color: navButton.selected ? ink900 : "#f4fbff" + font.pixelSize: compactLayout ? 14 : 15 + font.weight: appBoldWeight + horizontalAlignment: Text.AlignLeft + } - Label { - Layout.fillWidth: true - text: modelData.title - color: navButton.selected ? ink900 : "#f4fbff" - font.pixelSize: compactLayout ? 14 : 15 - font.weight: appBoldWeight - horizontalAlignment: Text.AlignLeft - } - - Label { - Layout.fillWidth: true - text: modelData.hint - visible: !denseLayout - color: navButton.selected ? ink500 : "#adc4cc" - font.pixelSize: compactLayout ? 10 : 11 - horizontalAlignment: Text.AlignLeft - } + Label { + Layout.fillWidth: true + text: modelData.hint + visible: !denseLayout + color: navButton.selected ? ink500 : "#adc4cc" + font.pixelSize: compactLayout ? 10 : 11 + horizontalAlignment: Text.AlignLeft } } + } MouseArea { id: navMouse @@ -845,7 +926,9 @@ ApplicationWindow { } } - Item { Layout.fillHeight: true } + Item { + Layout.fillHeight: true + } PrimaryButton { Layout.fillWidth: true @@ -894,11 +977,7 @@ ApplicationWindow { Label { visible: !slimLayout - text: ["筛选记录、导出数据并快速查看考勤状态。", - "按时间区间查看签到情况,导出统计报表。", - "集中管理时间段、映射配置和连接信息。", - "同步照片、调整学号映射并执行删除操作。", - "了解系统用途、功能范围和运行信息。"][window.currentPageIndex] + text: ["筛选记录、导出数据并快速查看考勤状态。", "按时间区间查看签到情况,导出统计报表。", "集中管理时间段、映射配置和连接信息。", "同步照片、调整学号映射并执行删除操作。", "了解系统用途、功能范围和运行信息。"][window.currentPageIndex] color: ink500 font.pixelSize: compactLayout ? 12 : 13 } @@ -964,11 +1043,26 @@ ApplicationWindow { onCurrentIndexChanged: window.switchPage(currentIndex) background: Item {} - MobileNavTabButton { width: navigationBar.tabWidth; text: "考勤记录" } - MobileNavTabButton { width: navigationBar.tabWidth; text: "统计分析" } - MobileNavTabButton { width: navigationBar.tabWidth; text: "配置中心" } - MobileNavTabButton { width: navigationBar.tabWidth; text: "人脸管理" } - MobileNavTabButton { width: navigationBar.tabWidth; text: "关于" } + MobileNavTabButton { + width: navigationBar.tabWidth + text: "考勤记录" + } + MobileNavTabButton { + width: navigationBar.tabWidth + text: "统计分析" + } + MobileNavTabButton { + width: navigationBar.tabWidth + text: "配置中心" + } + MobileNavTabButton { + width: navigationBar.tabWidth + text: "人脸管理" + } + MobileNavTabButton { + width: navigationBar.tabWidth + text: "关于" + } } StackLayout { @@ -983,8 +1077,19 @@ ApplicationWindow { visible: StackLayout.isCurrentItem opacity: StackLayout.isCurrentItem ? 1 : 0 scale: resizingWindow || StackLayout.isCurrentItem ? 1 : 0.985 - Behavior on opacity { enabled: !resizingWindow; NumberAnimation { duration: 140 } } - Behavior on scale { enabled: !resizingWindow; NumberAnimation { duration: 180; easing.type: Easing.OutCubic } } + Behavior on opacity { + enabled: !resizingWindow + NumberAnimation { + duration: 140 + } + } + Behavior on scale { + enabled: !resizingWindow + NumberAnimation { + duration: 180 + easing.type: Easing.OutCubic + } + } ColumnLayout { anchors.fill: parent @@ -1011,7 +1116,10 @@ ApplicationWindow { anchors.leftMargin: slimLayout ? 34 : 44 anchors.rightMargin: slimLayout ? 12 : 18 spacing: 6 - Label { text: "总记录数"; color: ink500 } + Label { + text: "总记录数" + color: ink500 + } Label { text: appController.totalRecords font.pixelSize: slimLayout ? 22 : (compactLayout ? 30 : 34) @@ -1046,7 +1154,10 @@ ApplicationWindow { anchors.leftMargin: slimLayout ? 34 : 44 anchors.rightMargin: slimLayout ? 12 : 18 spacing: 6 - Label { text: "今日签到"; color: ink500 } + Label { + text: "今日签到" + color: ink500 + } Label { text: appController.todayRecords font.pixelSize: slimLayout ? 22 : (compactLayout ? 30 : 34) @@ -1081,7 +1192,10 @@ ApplicationWindow { anchors.leftMargin: slimLayout ? 34 : 44 anchors.rightMargin: slimLayout ? 12 : 18 spacing: 6 - Label { text: "筛选结果"; color: ink500 } + Label { + text: "筛选结果" + color: ink500 + } Label { text: appController.filteredCount + " / " + appController.loadedRecords font.pixelSize: slimLayout ? 20 : (compactLayout ? 26 : 30) @@ -1102,65 +1216,93 @@ ApplicationWindow { } } - Rectangle { - Layout.fillWidth: true - radius: 8 - color: panelStrong - border.color: lineColor - Layout.preferredHeight: phoneLayout ? 236 : (slimLayout ? 174 : 92) - clip: true + Rectangle { + Layout.fillWidth: true + radius: 8 + color: panelStrong + border.color: lineColor + Layout.preferredHeight: phoneLayout ? (dateFilter.currentIndex === 4 ? 300 : 236) : (slimLayout ? (dateFilter.currentIndex === 4 ? 300 : 220) : 98) + clip: true - GridLayout { - anchors.fill: parent - anchors.margins: slimLayout ? 12 : 14 - columns: slimLayout ? 2 : (dateFilter.currentIndex === 4 ? 6 : 5) - columnSpacing: 12 - rowSpacing: 12 + GridLayout { + anchors.fill: parent + anchors.margins: slimLayout ? 12 : 14 + columns: slimLayout ? 2 : (dateFilter.currentIndex === 4 ? 6 : 5) + columnSpacing: 12 + rowSpacing: 12 - Column { - Layout.fillWidth: true - Layout.preferredWidth: phoneLayout ? 1 : 150 - spacing: 6 - Label { text: "班级"; color: ink700; font.weight: appBoldWeight; font.pixelSize: 12 } + Column { + Layout.fillWidth: true + Layout.columnSpan: 1 + Layout.preferredWidth: slimLayout ? 1 : 170 + Layout.minimumWidth: slimLayout ? 0 : 140 + spacing: 6 + Label { + text: "班级" + color: ink700 + font.weight: appBoldWeight + font.pixelSize: 12 + } AppComboBox { id: classFilter width: parent.width model: appController.classOptions onCurrentTextChanged: applyRecordFilters() } - } + } - Column { - Layout.fillWidth: true - Layout.preferredWidth: phoneLayout ? 1 : 160 - spacing: 6 - Label { text: "学号"; color: ink700; font.weight: appBoldWeight; font.pixelSize: 12 } + Column { + Layout.fillWidth: true + Layout.columnSpan: 1 + Layout.preferredWidth: slimLayout ? 1 : 170 + Layout.minimumWidth: slimLayout ? 0 : 140 + spacing: 6 + Label { + text: "学号" + color: ink700 + font.weight: appBoldWeight + font.pixelSize: 12 + } AppComboBox { id: studentFilter width: parent.width model: appController.studentOptions onCurrentTextChanged: applyRecordFilters() } - } + } - Column { - Layout.fillWidth: true - Layout.preferredWidth: phoneLayout ? 1 : 190 - spacing: 6 - Label { text: "姓名"; color: ink700; font.weight: appBoldWeight; font.pixelSize: 12 } + Column { + Layout.fillWidth: true + Layout.columnSpan: 1 + Layout.preferredWidth: slimLayout ? 1 : 230 + Layout.minimumWidth: slimLayout ? 0 : 180 + spacing: 6 + Label { + text: "姓名" + color: ink700 + font.weight: appBoldWeight + font.pixelSize: 12 + } AppTextField { id: nameFilter width: parent.width placeholderText: "输入姓名关键字" onTextChanged: applyRecordFilters() } - } + } - Column { - Layout.fillWidth: true - Layout.preferredWidth: phoneLayout ? 1 : 160 - spacing: 6 - Label { text: "日期"; color: ink700; font.weight: appBoldWeight; font.pixelSize: 12 } + Column { + Layout.fillWidth: true + Layout.columnSpan: 1 + Layout.preferredWidth: slimLayout ? 1 : 170 + Layout.minimumWidth: slimLayout ? 0 : 140 + spacing: 6 + Label { + text: "日期" + color: ink700 + font.weight: appBoldWeight + font.pixelSize: 12 + } AppComboBox { id: dateFilter width: parent.width @@ -1168,30 +1310,46 @@ ApplicationWindow { currentIndex: 0 onCurrentIndexChanged: applyRecordFilters() } - } + } - Column { - Layout.fillWidth: true - Layout.preferredWidth: phoneLayout ? 1 : 150 - width: dateFilter.currentIndex === 4 ? implicitWidth : 0 - height: dateFilter.currentIndex === 4 ? implicitHeight : 0 - visible: dateFilter.currentIndex === 4 + Column { + Layout.fillWidth: true + Layout.columnSpan: 1 + Layout.preferredWidth: slimLayout ? 1 : 170 + Layout.minimumWidth: slimLayout ? 0 : 140 + width: dateFilter.currentIndex === 4 ? implicitWidth : 0 + height: dateFilter.currentIndex === 4 ? implicitHeight : 0 + visible: dateFilter.currentIndex === 4 spacing: 6 - Label { text: "指定日期"; color: ink700; font.weight: appBoldWeight; font.pixelSize: 12 } + Label { + text: "指定日期" + color: ink700 + font.weight: appBoldWeight + font.pixelSize: 12 + } AppTextField { id: specificDateField width: parent.width text: Qt.formatDate(new Date(), "yyyy-MM-dd") placeholderText: "yyyy-MM-dd" - onTextChanged: if (visible) applyRecordFilters() + onTextChanged: if (visible) + applyRecordFilters() } } + Item { + Layout.columnSpan: slimLayout ? 2 : 1 + Layout.fillWidth: slimLayout + Layout.preferredWidth: slimLayout ? 1 : (dateFilter.currentIndex === 4 ? 300 : 330) + Layout.minimumWidth: slimLayout ? 0 : 280 + Layout.preferredHeight: slimLayout ? 42 : 64 + Layout.alignment: Qt.AlignBottom + GridLayout { - Layout.columnSpan: slimLayout ? 2 : 1 - Layout.fillWidth: true - Layout.alignment: Qt.AlignBottom - columns: phoneLayout ? 2 : 2 + anchors.bottom: parent.bottom + anchors.right: parent.right + width: parent.width + columns: 2 columnSpacing: 8 rowSpacing: 10 @@ -1209,6 +1367,7 @@ ApplicationWindow { onClicked: clearDialog.open() } } + } } } @@ -1232,7 +1391,7 @@ ApplicationWindow { ColumnLayout { anchors.fill: parent anchors.margins: 16 - spacing: 8 + spacing: 8 Rectangle { Layout.fillWidth: true @@ -1240,13 +1399,18 @@ ApplicationWindow { radius: 8 color: "#eef2f6" - RowLayout { - anchors.fill: parent - anchors.leftMargin: 16 - anchors.rightMargin: 16 - spacing: 12 + RowLayout { + anchors.fill: parent + anchors.leftMargin: 16 + anchors.rightMargin: 16 + spacing: 12 - Label { text: "签到时间"; Layout.preferredWidth: recordTableCard.timeColWidth; font.weight: appBoldWeight; color: ink900 } + Label { + text: "签到时间" + Layout.preferredWidth: recordTableCard.timeColWidth + font.weight: appBoldWeight + color: ink900 + } Label { text: "班级" visible: recordTableCard.showClassColumn @@ -1254,8 +1418,18 @@ ApplicationWindow { font.weight: appBoldWeight color: ink900 } - Label { text: "学号"; Layout.preferredWidth: recordTableCard.studentColWidth; font.weight: appBoldWeight; color: ink900 } - Label { text: "姓名"; Layout.preferredWidth: recordTableCard.nameColWidth; font.weight: appBoldWeight; color: ink900 } + Label { + text: "学号" + Layout.preferredWidth: recordTableCard.studentColWidth + font.weight: appBoldWeight + color: ink900 + } + Label { + text: "姓名" + Layout.preferredWidth: recordTableCard.nameColWidth + font.weight: appBoldWeight + color: ink900 + } Label { text: "置信度" visible: recordTableCard.showConfidenceColumn @@ -1263,8 +1437,15 @@ ApplicationWindow { font.weight: appBoldWeight color: ink900 } - Label { text: "状态"; Layout.preferredWidth: recordTableCard.statusColWidth; font.weight: appBoldWeight; color: ink900 } - Item { Layout.fillWidth: true } + Label { + text: "状态" + Layout.preferredWidth: recordTableCard.statusColWidth + font.weight: appBoldWeight + color: ink900 + } + Item { + Layout.fillWidth: true + } } } @@ -1287,48 +1468,78 @@ ApplicationWindow { color: hovered ? "#f6f9fc" : (index % 2 === 0 ? "#ffffff" : "#fafbfc") border.color: hovered ? "#cbd5e1" : "#edf0f3" - Behavior on color { enabled: !resizingWindow; ColorAnimation { duration: 120 } } - Behavior on border.color { enabled: !resizingWindow; ColorAnimation { duration: 120 } } - - HoverHandler { id: recordHoverHandler } - - RowLayout { - anchors.fill: parent - anchors.leftMargin: 16 - anchors.rightMargin: 16 - spacing: 12 - - Label { text: model.timestamp; Layout.preferredWidth: recordTableCard.timeColWidth; color: ink900; elide: Text.ElideRight } - Label { - text: model.className - visible: recordTableCard.showClassColumn - Layout.preferredWidth: recordTableCard.classColWidth - color: ink700 - } - Label { text: model.studentId; Layout.preferredWidth: recordTableCard.studentColWidth; color: ink700; elide: Text.ElideRight } - Label { text: model.name; Layout.preferredWidth: recordTableCard.nameColWidth; color: ink900; font.weight: appBoldWeight; elide: Text.ElideRight } - Label { - text: model.confidenceText - visible: recordTableCard.showConfidenceColumn - Layout.preferredWidth: recordTableCard.confidenceColWidth - color: ink700 - } - Rectangle { - Layout.preferredWidth: recordTableCard.statusColWidth - Layout.preferredHeight: 26 - radius: 8 - color: model.success ? "#dbeedb" : "#f6d9d4" - - Label { - anchors.centerIn: parent - text: model.statusText - color: model.success ? successColor : dangerColor - font.weight: appBoldWeight + Behavior on color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } + } + Behavior on border.color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } + } + + HoverHandler { + id: recordHoverHandler + } + + RowLayout { + anchors.fill: parent + anchors.leftMargin: 16 + anchors.rightMargin: 16 + spacing: 12 + + Label { + text: model.timestamp + Layout.preferredWidth: recordTableCard.timeColWidth + color: ink900 + elide: Text.ElideRight + } + Label { + text: model.className + visible: recordTableCard.showClassColumn + Layout.preferredWidth: recordTableCard.classColWidth + color: ink700 + } + Label { + text: model.studentId + Layout.preferredWidth: recordTableCard.studentColWidth + color: ink700 + elide: Text.ElideRight + } + Label { + text: model.name + Layout.preferredWidth: recordTableCard.nameColWidth + color: ink900 + font.weight: appBoldWeight + elide: Text.ElideRight + } + Label { + text: model.confidenceText + visible: recordTableCard.showConfidenceColumn + Layout.preferredWidth: recordTableCard.confidenceColWidth + color: ink700 + } + Rectangle { + Layout.preferredWidth: recordTableCard.statusColWidth + Layout.preferredHeight: 26 + radius: 8 + color: model.success ? "#dbeedb" : "#f6d9d4" + + Label { + anchors.centerIn: parent + text: model.statusText + color: model.success ? successColor : dangerColor + font.weight: appBoldWeight + } + } + Item { + Layout.fillWidth: true } } - Item { Layout.fillWidth: true } } - } ScrollBar.vertical: AppScrollBar {} } @@ -1353,8 +1564,19 @@ ApplicationWindow { visible: StackLayout.isCurrentItem opacity: StackLayout.isCurrentItem ? 1 : 0 scale: resizingWindow || StackLayout.isCurrentItem ? 1 : 0.985 - Behavior on opacity { enabled: !resizingWindow; NumberAnimation { duration: 140 } } - Behavior on scale { enabled: !resizingWindow; NumberAnimation { duration: 180; easing.type: Easing.OutCubic } } + Behavior on opacity { + enabled: !resizingWindow + NumberAnimation { + duration: 140 + } + } + Behavior on scale { + enabled: !resizingWindow + NumberAnimation { + duration: 180 + easing.type: Easing.OutCubic + } + } ColumnLayout { anchors.fill: parent @@ -1365,33 +1587,33 @@ ApplicationWindow { radius: 8 color: panelStrong border.color: lineColor - Layout.preferredHeight: phoneLayout ? 150 : (slimLayout ? 150 : 78) + Layout.preferredHeight: phoneLayout ? 150 : (slimLayout ? 150 : 78) clip: true GridLayout { id: statisticsFilterGrid anchors.fill: parent anchors.margins: slimLayout ? 12 : 18 - columns: slimLayout ? 2 : 5 + columns: slimLayout ? 2 : 4 rowSpacing: 10 columnSpacing: 12 AppComboBox { id: quickStatisticsFilter Layout.fillWidth: true - Layout.preferredWidth: phoneLayout ? 1 : 150 + Layout.preferredWidth: 1 model: ["今天", "最近7天", "最近30天", "本月"] currentIndex: 2 onCurrentTextChanged: { - statisticsPreset = currentText - applyStatisticsPreset() + statisticsPreset = currentText; + applyStatisticsPreset(); } } AppTextField { id: statsStartField Layout.fillWidth: true - Layout.preferredWidth: phoneLayout ? 1 : 150 + Layout.preferredWidth: 1 placeholderText: "开始日期" onEditingFinished: appController.updateStatisticsRange(text, statsEndField.text) } @@ -1399,14 +1621,14 @@ ApplicationWindow { AppTextField { id: statsEndField Layout.fillWidth: true - Layout.preferredWidth: phoneLayout ? 1 : 150 + Layout.preferredWidth: 1 placeholderText: "结束日期" onEditingFinished: appController.updateStatisticsRange(statsStartField.text, text) } SecondaryButton { Layout.fillWidth: true - Layout.preferredWidth: phoneLayout ? 1 : implicitWidth + Layout.preferredWidth: 1 text: "刷新统计" onClicked: appController.updateStatisticsRange(statsStartField.text, statsEndField.text) } @@ -1421,7 +1643,7 @@ ApplicationWindow { Rectangle { Layout.fillWidth: true - Layout.preferredWidth: phoneLayout ? 1 : 100 + Layout.preferredWidth: 1 Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100) radius: 8 color: panelStrong @@ -1435,8 +1657,16 @@ ApplicationWindow { anchors.leftMargin: slimLayout ? 34 : 44 anchors.rightMargin: slimLayout ? 12 : 18 spacing: 6 - Label { text: "统计学生数"; color: ink500 } - Label { text: appController.statisticsStudentCount; font.pixelSize: slimLayout ? 22 : (compactLayout ? 28 : 32); font.weight: appBoldWeight; color: ink900 } + Label { + text: "统计学生数" + color: ink500 + } + Label { + text: appController.statisticsStudentCount + font.pixelSize: slimLayout ? 22 : (compactLayout ? 28 : 32) + font.weight: appBoldWeight + color: ink900 + } } Rectangle { @@ -1452,7 +1682,7 @@ ApplicationWindow { Rectangle { Layout.fillWidth: true - Layout.preferredWidth: phoneLayout ? 1 : 100 + Layout.preferredWidth: 1 Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100) radius: 8 color: panelStrong @@ -1466,8 +1696,16 @@ ApplicationWindow { anchors.leftMargin: slimLayout ? 34 : 44 anchors.rightMargin: slimLayout ? 12 : 18 spacing: 6 - Label { text: "统计记录数"; color: ink500 } - Label { text: appController.statisticsRecordCount; font.pixelSize: slimLayout ? 22 : (compactLayout ? 28 : 32); font.weight: appBoldWeight; color: tealColor } + Label { + text: "统计记录数" + color: ink500 + } + Label { + text: appController.statisticsRecordCount + font.pixelSize: slimLayout ? 22 : (compactLayout ? 28 : 32) + font.weight: appBoldWeight + color: tealColor + } } Rectangle { @@ -1484,6 +1722,7 @@ ApplicationWindow { Rectangle { Layout.columnSpan: phoneLayout ? 2 : 1 Layout.fillWidth: true + Layout.preferredWidth: 1 Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100) radius: 8 color: panelStrong @@ -1497,7 +1736,10 @@ ApplicationWindow { anchors.leftMargin: slimLayout ? 34 : 44 anchors.rightMargin: slimLayout ? 12 : 18 spacing: 6 - Label { text: "统计范围"; color: ink500 } + Label { + text: "统计范围" + color: ink500 + } Label { text: appController.statisticsRangeText font.pixelSize: slimLayout ? 14 : (compactLayout ? 18 : 20) @@ -1540,7 +1782,7 @@ ApplicationWindow { ColumnLayout { anchors.fill: parent anchors.margins: 16 - spacing: 8 + spacing: 8 Rectangle { Layout.fillWidth: true @@ -1554,9 +1796,21 @@ ApplicationWindow { anchors.rightMargin: 16 spacing: 12 - Label { text: "学号"; Layout.preferredWidth: statisticsTableCard.studentColWidth; font.weight: appBoldWeight } - Label { text: "姓名"; Layout.preferredWidth: statisticsTableCard.nameColWidth; font.weight: appBoldWeight } - Label { text: "总签到次数"; Layout.preferredWidth: statisticsTableCard.totalColWidth; font.weight: appBoldWeight } + Label { + text: "学号" + Layout.preferredWidth: statisticsTableCard.studentColWidth + font.weight: appBoldWeight + } + Label { + text: "姓名" + Layout.preferredWidth: statisticsTableCard.nameColWidth + font.weight: appBoldWeight + } + Label { + text: "总签到次数" + Layout.preferredWidth: statisticsTableCard.totalColWidth + font.weight: appBoldWeight + } Label { text: "今日签到" visible: statisticsTableCard.showTodayColumn @@ -1575,7 +1829,9 @@ ApplicationWindow { Layout.preferredWidth: statisticsTableCard.avgColWidth font.weight: appBoldWeight } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } } } @@ -1598,42 +1854,69 @@ ApplicationWindow { color: hovered ? "#f7f5e8" : (index % 2 === 0 ? "#ffffff" : "#f8f3ec") border.color: hovered ? "#e4d6aa" : (index % 2 === 0 ? "#ece4da" : "#e7ded3") - Behavior on color { enabled: !resizingWindow; ColorAnimation { duration: 120 } } - Behavior on border.color { enabled: !resizingWindow; ColorAnimation { duration: 120 } } - - HoverHandler { id: statisticsHoverHandler } - - RowLayout { - anchors.fill: parent - anchors.leftMargin: 16 - anchors.rightMargin: 16 - spacing: 12 - - Label { text: model.studentId; Layout.preferredWidth: statisticsTableCard.studentColWidth; color: ink700 } - Label { text: model.name; Layout.preferredWidth: statisticsTableCard.nameColWidth; color: ink900; font.weight: appBoldWeight } - Label { text: model.totalCount; Layout.preferredWidth: statisticsTableCard.totalColWidth; color: ink700 } - Label { - text: model.todayCount - visible: statisticsTableCard.showTodayColumn - Layout.preferredWidth: statisticsTableCard.todayColWidth - color: ink700 + Behavior on color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } } - Label { - text: model.lastCheckIn - visible: statisticsTableCard.showLastColumn - Layout.preferredWidth: statisticsTableCard.lastColWidth - color: ink700 + Behavior on border.color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } } - Label { - text: model.averageConfidenceText - visible: statisticsTableCard.showAverageColumn - Layout.preferredWidth: statisticsTableCard.avgColWidth - color: tealColor - font.weight: appBoldWeight + + HoverHandler { + id: statisticsHoverHandler + } + + RowLayout { + anchors.fill: parent + anchors.leftMargin: 16 + anchors.rightMargin: 16 + spacing: 12 + + Label { + text: model.studentId + Layout.preferredWidth: statisticsTableCard.studentColWidth + color: ink700 + } + Label { + text: model.name + Layout.preferredWidth: statisticsTableCard.nameColWidth + color: ink900 + font.weight: appBoldWeight + } + Label { + text: model.totalCount + Layout.preferredWidth: statisticsTableCard.totalColWidth + color: ink700 + } + Label { + text: model.todayCount + visible: statisticsTableCard.showTodayColumn + Layout.preferredWidth: statisticsTableCard.todayColWidth + color: ink700 + } + Label { + text: model.lastCheckIn + visible: statisticsTableCard.showLastColumn + Layout.preferredWidth: statisticsTableCard.lastColWidth + color: ink700 + } + Label { + text: model.averageConfidenceText + visible: statisticsTableCard.showAverageColumn + Layout.preferredWidth: statisticsTableCard.avgColWidth + color: tealColor + font.weight: appBoldWeight + } + Item { + Layout.fillWidth: true + } } - Item { Layout.fillWidth: true } } - } ScrollBar.vertical: AppScrollBar {} } @@ -1658,8 +1941,19 @@ ApplicationWindow { visible: StackLayout.isCurrentItem opacity: StackLayout.isCurrentItem ? 1 : 0 scale: resizingWindow || StackLayout.isCurrentItem ? 1 : 0.985 - Behavior on opacity { enabled: !resizingWindow; NumberAnimation { duration: 140 } } - Behavior on scale { enabled: !resizingWindow; NumberAnimation { duration: 180; easing.type: Easing.OutCubic } } + Behavior on opacity { + enabled: !resizingWindow + NumberAnimation { + duration: 140 + } + } + Behavior on scale { + enabled: !resizingWindow + NumberAnimation { + duration: 180 + easing.type: Easing.OutCubic + } + } ScrollView { id: configScrollView @@ -1723,9 +2017,7 @@ ApplicationWindow { radius: 8 color: panelStrong border.color: lineColor - Layout.preferredHeight: timeSlotsExpanded - ? 18 + 64 + 12 + 42 + 12 + Math.max(52, timeSlotListView.contentHeight) + 18 - : 100 + Layout.preferredHeight: timeSlotsExpanded ? 18 + 64 + 12 + 42 + 12 + Math.max(52, timeSlotListView.contentHeight) + 18 : 100 clip: true ColumnLayout { @@ -1760,7 +2052,10 @@ ApplicationWindow { SecondaryButton { visible: timeSlotsExpanded text: "恢复默认" - onClicked: { appController.timeSlotModel.resetToDefault(); appController.saveTimeSlots() } + onClicked: { + appController.timeSlotModel.resetToDefault(); + appController.saveTimeSlots(); + } } SecondaryButton { visible: timeSlotsExpanded @@ -1786,11 +2081,29 @@ ApplicationWindow { anchors.fill: parent anchors.leftMargin: 16 anchors.rightMargin: 16 - Label { text: "名称"; Layout.preferredWidth: 220; font.weight: appBoldWeight } - Label { text: "开始"; Layout.preferredWidth: 120; font.weight: appBoldWeight } - Label { text: "结束"; Layout.preferredWidth: 120; font.weight: appBoldWeight } - Label { text: "状态"; Layout.preferredWidth: 100; font.weight: appBoldWeight } - Item { Layout.fillWidth: true } + Label { + text: "名称" + Layout.preferredWidth: 220 + font.weight: appBoldWeight + } + Label { + text: "开始" + Layout.preferredWidth: 120 + font.weight: appBoldWeight + } + Label { + text: "结束" + Layout.preferredWidth: 120 + font.weight: appBoldWeight + } + Label { + text: "状态" + Layout.preferredWidth: 100 + font.weight: appBoldWeight + } + Item { + Layout.fillWidth: true + } } } @@ -1812,10 +2125,22 @@ ApplicationWindow { color: hovered ? "#f9f1ea" : (index % 2 === 0 ? "#ffffff" : "#f8f3ec") border.color: hovered ? "#e5cfc1" : (index % 2 === 0 ? "#ece4da" : "#e7ded3") - Behavior on color { enabled: !resizingWindow; ColorAnimation { duration: 120 } } - Behavior on border.color { enabled: !resizingWindow; ColorAnimation { duration: 120 } } + Behavior on color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } + } + Behavior on border.color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } + } - HoverHandler { id: timeSlotHoverHandler } + HoverHandler { + id: timeSlotHoverHandler + } RowLayout { anchors.fill: parent @@ -1823,18 +2148,38 @@ ApplicationWindow { anchors.rightMargin: 16 spacing: 12 - Label { text: model.name; Layout.preferredWidth: 220 } - Label { text: model.startTime; Layout.preferredWidth: 120 } - Label { text: model.endTime; Layout.preferredWidth: 120 } + Label { + text: model.name + Layout.preferredWidth: 220 + } + Label { + text: model.startTime + Layout.preferredWidth: 120 + } + Label { + text: model.endTime + Layout.preferredWidth: 120 + } Label { text: model.enabled ? "启用" : "禁用" Layout.preferredWidth: 100 color: model.enabled ? successColor : goldColor font.weight: appBoldWeight } - Item { Layout.fillWidth: true } - SecondaryButton { text: "编辑"; onClicked: openTimeSlotDialog(index) } - SecondaryButton { text: "删除"; onClicked: { appController.timeSlotModel.removeTimeSlot(index); appController.saveTimeSlots() } } + Item { + Layout.fillWidth: true + } + SecondaryButton { + text: "编辑" + onClicked: openTimeSlotDialog(index) + } + SecondaryButton { + text: "删除" + onClicked: { + appController.timeSlotModel.removeTimeSlot(index); + appController.saveTimeSlots(); + } + } } } } @@ -1846,11 +2191,7 @@ ApplicationWindow { radius: 8 color: panelStrong border.color: lineColor - Layout.preferredHeight: mappingsExpanded - ? 18 + 64 + 12 + mappingTabs.implicitHeight + 12 - + Math.max(64, mappingTabs.currentIndex === 0 ? studentMappingListView.contentHeight - : classMappingListView.contentHeight) + 18 - : 100 + Layout.preferredHeight: mappingsExpanded ? 18 + 64 + 12 + mappingTabs.implicitHeight + 12 + Math.max(64, mappingTabs.currentIndex === 0 ? studentMappingListView.contentHeight : classMappingListView.contentHeight) + 18 : 100 clip: true ColumnLayout { @@ -1914,8 +2255,7 @@ ApplicationWindow { StackLayout { visible: mappingsExpanded Layout.fillWidth: true - Layout.preferredHeight: visible ? (mappingTabs.currentIndex === 0 ? studentMappingListView.contentHeight - : classMappingListView.contentHeight) : 0 + Layout.preferredHeight: visible ? (mappingTabs.currentIndex === 0 ? studentMappingListView.contentHeight : classMappingListView.contentHeight) : 0 currentIndex: mappingTabs.currentIndex Rectangle { @@ -1938,7 +2278,9 @@ ApplicationWindow { onClicked: appController.studentMappingModel.addEntry("", "") } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } } delegate: Rectangle { @@ -1949,10 +2291,22 @@ ApplicationWindow { color: hovered ? "#f9f0ea" : (index % 2 === 0 ? "#ffffff" : "#f8f3ec") border.color: hovered ? "#e5cfc1" : (index % 2 === 0 ? "#ece4da" : "#e7ded3") - Behavior on color { enabled: !resizingWindow; ColorAnimation { duration: 120 } } - Behavior on border.color { enabled: !resizingWindow; ColorAnimation { duration: 120 } } + Behavior on color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } + } + Behavior on border.color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } + } - HoverHandler { id: studentMappingHoverHandler } + HoverHandler { + id: studentMappingHoverHandler + } RowLayout { anchors.fill: parent @@ -1976,8 +2330,13 @@ ApplicationWindow { onEditingFinished: appController.studentMappingModel.updateEntry(index, studentKeyField.text, text) } - Item { Layout.fillWidth: true } - SecondaryButton { text: "删除"; onClicked: appController.studentMappingModel.removeEntry(index) } + Item { + Layout.fillWidth: true + } + SecondaryButton { + text: "删除" + onClicked: appController.studentMappingModel.removeEntry(index) + } } } } @@ -2003,7 +2362,9 @@ ApplicationWindow { onClicked: appController.classMappingModel.addEntry("", "") } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } } delegate: Rectangle { @@ -2014,10 +2375,22 @@ ApplicationWindow { 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 } } + Behavior on color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } + } + Behavior on border.color { + enabled: !resizingWindow + ColorAnimation { + duration: 120 + } + } - HoverHandler { id: classMappingHoverHandler } + HoverHandler { + id: classMappingHoverHandler + } RowLayout { anchors.fill: parent @@ -2041,8 +2414,13 @@ ApplicationWindow { onEditingFinished: appController.classMappingModel.updateEntry(index, classKeyField.text, text) } - Item { Layout.fillWidth: true } - SecondaryButton { text: "删除"; onClicked: appController.classMappingModel.removeEntry(index) } + Item { + Layout.fillWidth: true + } + SecondaryButton { + text: "删除" + onClicked: appController.classMappingModel.removeEntry(index) + } } } } @@ -2065,8 +2443,19 @@ ApplicationWindow { visible: StackLayout.isCurrentItem opacity: StackLayout.isCurrentItem ? 1 : 0 scale: resizingWindow || StackLayout.isCurrentItem ? 1 : 0.985 - Behavior on opacity { enabled: !resizingWindow; NumberAnimation { duration: 140 } } - Behavior on scale { enabled: !resizingWindow; NumberAnimation { duration: 180; easing.type: Easing.OutCubic } } + Behavior on opacity { + enabled: !resizingWindow + NumberAnimation { + duration: 140 + } + } + Behavior on scale { + enabled: !resizingWindow + NumberAnimation { + duration: 180 + easing.type: Easing.OutCubic + } + } ColumnLayout { anchors.fill: parent @@ -2099,7 +2488,10 @@ ApplicationWindow { Layout.alignment: phoneLayout ? Qt.AlignLeft : (Qt.AlignRight | Qt.AlignVCenter) spacing: 8 - SecondaryButton { text: "刷新"; onClicked: appController.refreshFaces() } + SecondaryButton { + text: "刷新" + onClicked: appController.refreshFaces() + } PrimaryButton { text: "保存映射" onClicked: appController.saveFaceMappings() @@ -2133,10 +2525,22 @@ ApplicationWindow { color: hovered ? "#fff6ef" : "#ffffff" border.color: hovered ? "#e6c8b8" : "#e8ddd0" - Behavior on color { enabled: !resizingWindow; ColorAnimation { duration: 140 } } - Behavior on border.color { enabled: !resizingWindow; ColorAnimation { duration: 140 } } + Behavior on color { + enabled: !resizingWindow + ColorAnimation { + duration: 140 + } + } + Behavior on border.color { + enabled: !resizingWindow + ColorAnimation { + duration: 140 + } + } - HoverHandler { id: faceHoverHandler } + HoverHandler { + id: faceHoverHandler + } ColumnLayout { anchors.fill: parent @@ -2197,15 +2601,17 @@ ApplicationWindow { } } - Item { Layout.fillHeight: true } + Item { + Layout.fillHeight: true + } DangerButton { Layout.fillWidth: true text: "删除该人脸" onClicked: { - pendingDeleteFaceId = model.faceId - deleteFaceRecordsCheck.checked = false - deleteFaceDialog.open() + pendingDeleteFaceId = model.faceId; + deleteFaceRecordsCheck.checked = false; + deleteFaceDialog.open(); } } } @@ -2232,8 +2638,19 @@ ApplicationWindow { visible: StackLayout.isCurrentItem opacity: StackLayout.isCurrentItem ? 1 : 0 scale: resizingWindow || StackLayout.isCurrentItem ? 1 : 0.985 - Behavior on opacity { enabled: !resizingWindow; NumberAnimation { duration: 140 } } - Behavior on scale { enabled: !resizingWindow; NumberAnimation { duration: 180; easing.type: Easing.OutCubic } } + Behavior on opacity { + enabled: !resizingWindow + NumberAnimation { + duration: 140 + } + } + Behavior on scale { + enabled: !resizingWindow + NumberAnimation { + duration: 180 + easing.type: Easing.OutCubic + } + } Rectangle { anchors.fill: parent @@ -2278,7 +2695,9 @@ ApplicationWindow { lineHeight: 1.25 } - Item { Layout.fillHeight: true } + Item { + Layout.fillHeight: true + } Rectangle { Layout.fillWidth: true @@ -2300,36 +2719,23 @@ ApplicationWindow { font.weight: appBoldWeight } - Label { + TextArea { Layout.fillWidth: true - text: "本系统为纪田野的个人毕业设计作品,仅用于毕业设计展示、学习研究与系统功能验证。" - wrapMode: Text.WordWrap + Layout.fillHeight: true + text: "本系统为纪田野的个人毕业设计作品,仅用于毕业设计展示、学习研究与系统功能验证。\nCopyright © 2026 纪田野。保留所有权利。\n仓库地址:https://git.yzfh.eu.cc/yzfh/qt.git\n查看账号:jtybishe 查看密码:jtybishe" + readOnly: true + selectByMouse: true + selectByKeyboard: true + wrapMode: TextEdit.WrapAnywhere color: ink700 - font.pixelSize: 13 - } - - Label { - Layout.fillWidth: true - text: "Copyright © 2026 纪田野。保留所有权利。" - wrapMode: Text.WordWrap - color: ink500 - font.pixelSize: 12 - } - - Label { - Layout.fillWidth: true - text: "仓库地址:https://git.yzfh.eu.cc/yzfh/qt.git" - wrapMode: Text.WrapAnywhere - color: ink500 - font.pixelSize: 12 - } - - Label { - Layout.fillWidth: true - text: "查看账号:jtybishe 查看密码:jtybishe" - wrapMode: Text.WordWrap - color: ink500 font.pixelSize: 12 + leftPadding: 0 + rightPadding: 0 + topPadding: 0 + bottomPadding: 0 + background: Rectangle { + color: "transparent" + } } } } @@ -2361,8 +2767,19 @@ ApplicationWindow { visible: opacity > 0 y: toastVisible ? parent.height - height - 28 : parent.height - height - 10 - Behavior on opacity { enabled: !resizingWindow; NumberAnimation { duration: 180 } } - Behavior on y { enabled: !resizingWindow; NumberAnimation { duration: 180; easing.type: Easing.OutCubic } } + Behavior on opacity { + enabled: !resizingWindow + NumberAnimation { + duration: 180 + } + } + Behavior on y { + enabled: !resizingWindow + NumberAnimation { + duration: 180 + easing.type: Easing.OutCubic + } + } RowLayout { anchors.fill: parent