完善移动端布局与发布配置

This commit is contained in:
2026-05-01 10:35:54 +08:00
parent 826cc4d56e
commit b4aa0082a0
6 changed files with 154 additions and 98 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Jitianye
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+4
View File
@@ -421,3 +421,7 @@ X-Encrypted-Response: true
- 如果服务端加密参数变化,需要同步修改 `src/CryptoUtils.cpp`。 - 如果服务端加密参数变化,需要同步修改 `src/CryptoUtils.cpp`。
- 如果要发布桌面版本,建议使用 Qt 部署工具收集运行时依赖。 - 如果要发布桌面版本,建议使用 Qt 部署工具收集运行时依赖。
- 如果要发布 Android 版本,建议补充签名配置和 release 构建流程。 - 如果要发布 Android 版本,建议补充签名配置和 release 构建流程。
## 13. 开源协议
本项目采用 MIT License 开源,详见 `LICENSE` 文件。
+5
View File
@@ -3,6 +3,7 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQmlContext> #include <QQmlContext>
#include <QQuickStyle> #include <QQuickStyle>
#include <QQuickWindow>
#include "src/AppController.h" #include "src/AppController.h"
@@ -24,6 +25,10 @@ int main(int argc, char *argv[])
QCoreApplication::setOrganizationName("STM32 Face Recognition"); QCoreApplication::setOrganizationName("STM32 Face Recognition");
QQuickStyle::setStyle("Basic"); QQuickStyle::setStyle("Basic");
#if defined(Q_OS_ANDROID)
QQuickWindow::setTextRenderType(QQuickWindow::NativeTextRendering);
#endif
AppController controller; AppController controller;
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
+102 -85
View File
@@ -29,6 +29,8 @@ ApplicationWindow {
readonly property color goldSoft: "#f5e8c6" readonly property color goldSoft: "#f5e8c6"
readonly property color successColor: "#1f7a4d" readonly property color successColor: "#1f7a4d"
readonly property color dangerColor: "#b9382f" readonly property color dangerColor: "#b9382f"
readonly property string appFontFamily: Qt.platform.os === "android" ? "sans-serif" : "Microsoft YaHei UI"
readonly property int appBoldWeight: Qt.platform.os === "android" ? Font.Medium : Font.Bold
property string statisticsPreset: "最近30天" property string statisticsPreset: "最近30天"
property int editingTimeSlotIndex: -1 property int editingTimeSlotIndex: -1
@@ -46,7 +48,7 @@ ApplicationWindow {
readonly property bool phoneLayout: width < 560 readonly property bool phoneLayout: width < 560
readonly property int sidebarWidth: slimLayout ? 0 : (compactLayout ? 212 : 240) readonly property int sidebarWidth: slimLayout ? 0 : (compactLayout ? 212 : 240)
font.family: "Microsoft YaHei UI" font.family: appFontFamily
onWidthChanged: { onWidthChanged: {
resizingWindow = true resizingWindow = true
@@ -174,7 +176,7 @@ ApplicationWindow {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: control.labelColor color: control.labelColor
font.pixelSize: 13 font.pixelSize: 13
font.bold: true font.weight: appBoldWeight
} }
} }
@@ -201,7 +203,7 @@ ApplicationWindow {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: control.labelColor color: control.labelColor
font.pixelSize: 13 font.pixelSize: 13
font.bold: true font.weight: appBoldWeight
} }
} }
@@ -292,7 +294,7 @@ ApplicationWindow {
text: badgeText text: badgeText
color: accent color: accent
font.pixelSize: emptyState.tightState ? 18 : 22 font.pixelSize: emptyState.tightState ? 18 : 22
font.bold: true font.weight: appBoldWeight
} }
} }
@@ -303,7 +305,7 @@ ApplicationWindow {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
color: ink900 color: ink900
font.pixelSize: emptyState.tightState ? 18 : 20 font.pixelSize: emptyState.tightState ? 18 : 20
font.bold: true font.weight: appBoldWeight
} }
Label { Label {
@@ -335,7 +337,7 @@ ApplicationWindow {
text: parent.textValue text: parent.textValue
color: parent.tintColor color: parent.tintColor
font.pixelSize: 11 font.pixelSize: 11
font.bold: true font.weight: appBoldWeight
} }
} }
@@ -367,7 +369,7 @@ ApplicationWindow {
Layout.fillWidth: true Layout.fillWidth: true
text: sectionHeader.title text: sectionHeader.title
font.pixelSize: phoneLayout ? 20 : 22 font.pixelSize: phoneLayout ? 20 : 22
font.bold: true font.weight: appBoldWeight
color: ink900 color: ink900
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
@@ -403,7 +405,7 @@ ApplicationWindow {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: control.checked ? "#f6fbfb" : ink700 color: control.checked ? "#f6fbfb" : ink700
font.pixelSize: 13 font.pixelSize: 13
font.bold: true font.weight: appBoldWeight
elide: Text.ElideRight elide: Text.ElideRight
} }
} }
@@ -426,7 +428,7 @@ ApplicationWindow {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: control.checked ? "#f6fbfb" : ink700 color: control.checked ? "#f6fbfb" : ink700
font.pixelSize: phoneLayout ? 12 : 13 font.pixelSize: phoneLayout ? 12 : 13
font.bold: true font.weight: appBoldWeight
elide: Text.ElideRight elide: Text.ElideRight
} }
} }
@@ -576,7 +578,7 @@ ApplicationWindow {
Label { Label {
text: "确定要清空服务器上的所有签到记录吗?" text: "确定要清空服务器上的所有签到记录吗?"
font.pixelSize: 20 font.pixelSize: 20
font.bold: true font.weight: appBoldWeight
color: ink900 color: ink900
} }
@@ -682,7 +684,7 @@ ApplicationWindow {
text: "确定删除 ID 为 " + pendingDeleteFaceId + " 的人脸信息吗?" text: "确定删除 ID 为 " + pendingDeleteFaceId + " 的人脸信息吗?"
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
font.pixelSize: 18 font.pixelSize: 18
font.bold: true font.weight: appBoldWeight
color: ink900 color: ink900
} }
@@ -734,7 +736,7 @@ ApplicationWindow {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
font.pixelSize: compactLayout ? 17 : 19 font.pixelSize: compactLayout ? 17 : 19
font.bold: true font.weight: appBoldWeight
color: "#ffffff" color: "#ffffff"
} }
} }
@@ -801,7 +803,7 @@ ApplicationWindow {
anchors.centerIn: parent anchors.centerIn: parent
text: modelData.icon text: modelData.icon
color: navButton.selected ? tealColor : "#d6e4e9" color: navButton.selected ? tealColor : "#d6e4e9"
font.bold: true font.weight: appBoldWeight
} }
} }
@@ -814,7 +816,7 @@ ApplicationWindow {
text: modelData.title text: modelData.title
color: navButton.selected ? ink900 : "#f4fbff" color: navButton.selected ? ink900 : "#f4fbff"
font.pixelSize: compactLayout ? 14 : 15 font.pixelSize: compactLayout ? 14 : 15
font.bold: true font.weight: appBoldWeight
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
} }
@@ -885,7 +887,7 @@ ApplicationWindow {
Layout.fillWidth: true Layout.fillWidth: true
text: ["考勤记录总览", "统计分析面板", "配置与同步中心", "人脸资料管理", "关于"][window.currentPageIndex] text: ["考勤记录总览", "统计分析面板", "配置与同步中心", "人脸资料管理", "关于"][window.currentPageIndex]
font.pixelSize: slimLayout ? 20 : (compactLayout ? 24 : 28) font.pixelSize: slimLayout ? 20 : (compactLayout ? 24 : 28)
font.bold: true font.weight: appBoldWeight
color: ink900 color: ink900
elide: Text.ElideRight elide: Text.ElideRight
} }
@@ -1013,7 +1015,7 @@ ApplicationWindow {
Label { Label {
text: appController.totalRecords text: appController.totalRecords
font.pixelSize: slimLayout ? 22 : (compactLayout ? 30 : 34) font.pixelSize: slimLayout ? 22 : (compactLayout ? 30 : 34)
font.bold: true font.weight: appBoldWeight
color: ink900 color: ink900
} }
} }
@@ -1048,7 +1050,7 @@ ApplicationWindow {
Label { Label {
text: appController.todayRecords text: appController.todayRecords
font.pixelSize: slimLayout ? 22 : (compactLayout ? 30 : 34) font.pixelSize: slimLayout ? 22 : (compactLayout ? 30 : 34)
font.bold: true font.weight: appBoldWeight
color: tealColor color: tealColor
} }
} }
@@ -1083,7 +1085,7 @@ ApplicationWindow {
Label { Label {
text: appController.filteredCount + " / " + appController.loadedRecords text: appController.filteredCount + " / " + appController.loadedRecords
font.pixelSize: slimLayout ? 20 : (compactLayout ? 26 : 30) font.pixelSize: slimLayout ? 20 : (compactLayout ? 26 : 30)
font.bold: true font.weight: appBoldWeight
color: goldColor color: goldColor
} }
} }
@@ -1117,9 +1119,9 @@ ApplicationWindow {
Column { Column {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 0 : 150 Layout.preferredWidth: phoneLayout ? 1 : 150
spacing: 6 spacing: 6
Label { text: "班级"; color: ink700; font.bold: true; font.pixelSize: 12 } Label { text: "班级"; color: ink700; font.weight: appBoldWeight; font.pixelSize: 12 }
AppComboBox { AppComboBox {
id: classFilter id: classFilter
width: parent.width width: parent.width
@@ -1130,9 +1132,9 @@ ApplicationWindow {
Column { Column {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 0 : 160 Layout.preferredWidth: phoneLayout ? 1 : 160
spacing: 6 spacing: 6
Label { text: "学号"; color: ink700; font.bold: true; font.pixelSize: 12 } Label { text: "学号"; color: ink700; font.weight: appBoldWeight; font.pixelSize: 12 }
AppComboBox { AppComboBox {
id: studentFilter id: studentFilter
width: parent.width width: parent.width
@@ -1143,9 +1145,9 @@ ApplicationWindow {
Column { Column {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 0 : 190 Layout.preferredWidth: phoneLayout ? 1 : 190
spacing: 6 spacing: 6
Label { text: "姓名"; color: ink700; font.bold: true; font.pixelSize: 12 } Label { text: "姓名"; color: ink700; font.weight: appBoldWeight; font.pixelSize: 12 }
AppTextField { AppTextField {
id: nameFilter id: nameFilter
width: parent.width width: parent.width
@@ -1156,9 +1158,9 @@ ApplicationWindow {
Column { Column {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 0 : 160 Layout.preferredWidth: phoneLayout ? 1 : 160
spacing: 6 spacing: 6
Label { text: "日期"; color: ink700; font.bold: true; font.pixelSize: 12 } Label { text: "日期"; color: ink700; font.weight: appBoldWeight; font.pixelSize: 12 }
AppComboBox { AppComboBox {
id: dateFilter id: dateFilter
width: parent.width width: parent.width
@@ -1170,12 +1172,12 @@ ApplicationWindow {
Column { Column {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 0 : 150 Layout.preferredWidth: phoneLayout ? 1 : 150
width: dateFilter.currentIndex === 4 ? implicitWidth : 0 width: dateFilter.currentIndex === 4 ? implicitWidth : 0
height: dateFilter.currentIndex === 4 ? implicitHeight : 0 height: dateFilter.currentIndex === 4 ? implicitHeight : 0
visible: dateFilter.currentIndex === 4 visible: dateFilter.currentIndex === 4
spacing: 6 spacing: 6
Label { text: "指定日期"; color: ink700; font.bold: true; font.pixelSize: 12 } Label { text: "指定日期"; color: ink700; font.weight: appBoldWeight; font.pixelSize: 12 }
AppTextField { AppTextField {
id: specificDateField id: specificDateField
width: parent.width width: parent.width
@@ -1185,29 +1187,28 @@ ApplicationWindow {
} }
} }
Flow { GridLayout {
Layout.columnSpan: slimLayout ? 2 : 1 Layout.columnSpan: slimLayout ? 2 : 1
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignBottom Layout.alignment: Qt.AlignBottom
height: implicitHeight columns: phoneLayout ? 2 : 2
spacing: 8 columnSpacing: 8
rowSpacing: 10
SecondaryButton { SecondaryButton {
text: "刷新" Layout.fillWidth: true
onClicked: appController.refreshRecords() Layout.preferredWidth: 1
} text: "刷新"
onClicked: appController.refreshRecords()
}
PrimaryButton { DangerButton {
text: "导出" Layout.fillWidth: true
fillColor: tealColor Layout.preferredWidth: 1
onClicked: recordExportDialog.open() text: "清空记录"
onClicked: clearDialog.open()
}
} }
DangerButton {
text: "清空记录"
onClicked: clearDialog.open()
}
}
} }
} }
@@ -1245,24 +1246,24 @@ ApplicationWindow {
anchors.rightMargin: 16 anchors.rightMargin: 16
spacing: 12 spacing: 12
Label { text: "签到时间"; Layout.preferredWidth: recordTableCard.timeColWidth; font.bold: true; color: ink900 } Label { text: "签到时间"; Layout.preferredWidth: recordTableCard.timeColWidth; font.weight: appBoldWeight; color: ink900 }
Label { Label {
text: "班级" text: "班级"
visible: recordTableCard.showClassColumn visible: recordTableCard.showClassColumn
Layout.preferredWidth: recordTableCard.classColWidth Layout.preferredWidth: recordTableCard.classColWidth
font.bold: true font.weight: appBoldWeight
color: ink900 color: ink900
} }
Label { text: "学号"; Layout.preferredWidth: recordTableCard.studentColWidth; font.bold: true; color: ink900 } Label { text: "学号"; Layout.preferredWidth: recordTableCard.studentColWidth; font.weight: appBoldWeight; color: ink900 }
Label { text: "姓名"; Layout.preferredWidth: recordTableCard.nameColWidth; font.bold: true; color: ink900 } Label { text: "姓名"; Layout.preferredWidth: recordTableCard.nameColWidth; font.weight: appBoldWeight; color: ink900 }
Label { Label {
text: "置信度" text: "置信度"
visible: recordTableCard.showConfidenceColumn visible: recordTableCard.showConfidenceColumn
Layout.preferredWidth: recordTableCard.confidenceColWidth Layout.preferredWidth: recordTableCard.confidenceColWidth
font.bold: true font.weight: appBoldWeight
color: ink900 color: ink900
} }
Label { text: "状态"; Layout.preferredWidth: recordTableCard.statusColWidth; font.bold: true; color: ink900 } Label { text: "状态"; Layout.preferredWidth: recordTableCard.statusColWidth; font.weight: appBoldWeight; color: ink900 }
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
} }
} }
@@ -1305,7 +1306,7 @@ ApplicationWindow {
color: ink700 color: ink700
} }
Label { text: model.studentId; Layout.preferredWidth: recordTableCard.studentColWidth; color: ink700; elide: Text.ElideRight } Label { text: model.studentId; Layout.preferredWidth: recordTableCard.studentColWidth; color: ink700; elide: Text.ElideRight }
Label { text: model.name; Layout.preferredWidth: recordTableCard.nameColWidth; color: ink900; font.bold: true; elide: Text.ElideRight } Label { text: model.name; Layout.preferredWidth: recordTableCard.nameColWidth; color: ink900; font.weight: appBoldWeight; elide: Text.ElideRight }
Label { Label {
text: model.confidenceText text: model.confidenceText
visible: recordTableCard.showConfidenceColumn visible: recordTableCard.showConfidenceColumn
@@ -1322,7 +1323,7 @@ ApplicationWindow {
anchors.centerIn: parent anchors.centerIn: parent
text: model.statusText text: model.statusText
color: model.success ? successColor : dangerColor color: model.success ? successColor : dangerColor
font.bold: true font.weight: appBoldWeight
} }
} }
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
@@ -1364,7 +1365,7 @@ ApplicationWindow {
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
Layout.preferredHeight: phoneLayout ? 186 : (slimLayout ? 150 : 78) Layout.preferredHeight: phoneLayout ? 150 : (slimLayout ? 150 : 78)
clip: true clip: true
GridLayout { GridLayout {
@@ -1378,7 +1379,7 @@ ApplicationWindow {
AppComboBox { AppComboBox {
id: quickStatisticsFilter id: quickStatisticsFilter
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 0 : 150 Layout.preferredWidth: phoneLayout ? 1 : 150
model: ["今天", "最近7天", "最近30天", "本月"] model: ["今天", "最近7天", "最近30天", "本月"]
currentIndex: 2 currentIndex: 2
onCurrentTextChanged: { onCurrentTextChanged: {
@@ -1390,7 +1391,7 @@ ApplicationWindow {
AppTextField { AppTextField {
id: statsStartField id: statsStartField
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 0 : 150 Layout.preferredWidth: phoneLayout ? 1 : 150
placeholderText: "开始日期" placeholderText: "开始日期"
onEditingFinished: appController.updateStatisticsRange(text, statsEndField.text) onEditingFinished: appController.updateStatisticsRange(text, statsEndField.text)
} }
@@ -1398,31 +1399,29 @@ ApplicationWindow {
AppTextField { AppTextField {
id: statsEndField id: statsEndField
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 0 : 150 Layout.preferredWidth: phoneLayout ? 1 : 150
placeholderText: "结束日期" placeholderText: "结束日期"
onEditingFinished: appController.updateStatisticsRange(statsStartField.text, text) onEditingFinished: appController.updateStatisticsRange(statsStartField.text, text)
} }
SecondaryButton { SecondaryButton {
Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 1 : implicitWidth
text: "刷新统计" text: "刷新统计"
onClicked: appController.updateStatisticsRange(statsStartField.text, statsEndField.text) onClicked: appController.updateStatisticsRange(statsStartField.text, statsEndField.text)
} }
PrimaryButton {
text: "导出统计报表"
onClicked: statisticsExportDialog.open()
}
} }
} }
GridLayout { GridLayout {
Layout.fillWidth: true Layout.fillWidth: true
columns: phoneLayout ? 1 : (slimLayout ? 3 : (compactLayout ? 1 : 3)) columns: phoneLayout ? 2 : (slimLayout ? 3 : (compactLayout ? 1 : 3))
rowSpacing: 10 rowSpacing: 10
columnSpacing: 14 columnSpacing: 14
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 1 : 100
Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100) Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100)
radius: 8 radius: 8
color: panelStrong color: panelStrong
@@ -1437,7 +1436,7 @@ ApplicationWindow {
anchors.rightMargin: slimLayout ? 12 : 18 anchors.rightMargin: slimLayout ? 12 : 18
spacing: 6 spacing: 6
Label { text: "统计学生数"; color: ink500 } Label { text: "统计学生数"; color: ink500 }
Label { text: appController.statisticsStudentCount; font.pixelSize: slimLayout ? 22 : (compactLayout ? 28 : 32); font.bold: true; color: ink900 } Label { text: appController.statisticsStudentCount; font.pixelSize: slimLayout ? 22 : (compactLayout ? 28 : 32); font.weight: appBoldWeight; color: ink900 }
} }
Rectangle { Rectangle {
@@ -1453,6 +1452,7 @@ ApplicationWindow {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 1 : 100
Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100) Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100)
radius: 8 radius: 8
color: panelStrong color: panelStrong
@@ -1467,7 +1467,7 @@ ApplicationWindow {
anchors.rightMargin: slimLayout ? 12 : 18 anchors.rightMargin: slimLayout ? 12 : 18
spacing: 6 spacing: 6
Label { text: "统计记录数"; color: ink500 } Label { text: "统计记录数"; color: ink500 }
Label { text: appController.statisticsRecordCount; font.pixelSize: slimLayout ? 22 : (compactLayout ? 28 : 32); font.bold: true; color: tealColor } Label { text: appController.statisticsRecordCount; font.pixelSize: slimLayout ? 22 : (compactLayout ? 28 : 32); font.weight: appBoldWeight; color: tealColor }
} }
Rectangle { Rectangle {
@@ -1482,6 +1482,7 @@ ApplicationWindow {
} }
Rectangle { Rectangle {
Layout.columnSpan: phoneLayout ? 2 : 1
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100) Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100)
radius: 8 radius: 8
@@ -1500,7 +1501,7 @@ ApplicationWindow {
Label { Label {
text: appController.statisticsRangeText text: appController.statisticsRangeText
font.pixelSize: slimLayout ? 14 : (compactLayout ? 18 : 20) font.pixelSize: slimLayout ? 14 : (compactLayout ? 18 : 20)
font.bold: true font.weight: appBoldWeight
color: goldColor color: goldColor
elide: Text.ElideRight elide: Text.ElideRight
} }
@@ -1553,26 +1554,26 @@ ApplicationWindow {
anchors.rightMargin: 16 anchors.rightMargin: 16
spacing: 12 spacing: 12
Label { text: "学号"; Layout.preferredWidth: statisticsTableCard.studentColWidth; font.bold: true } Label { text: "学号"; Layout.preferredWidth: statisticsTableCard.studentColWidth; font.weight: appBoldWeight }
Label { text: "姓名"; Layout.preferredWidth: statisticsTableCard.nameColWidth; font.bold: true } Label { text: "姓名"; Layout.preferredWidth: statisticsTableCard.nameColWidth; font.weight: appBoldWeight }
Label { text: "总签到次数"; Layout.preferredWidth: statisticsTableCard.totalColWidth; font.bold: true } Label { text: "总签到次数"; Layout.preferredWidth: statisticsTableCard.totalColWidth; font.weight: appBoldWeight }
Label { Label {
text: "今日签到" text: "今日签到"
visible: statisticsTableCard.showTodayColumn visible: statisticsTableCard.showTodayColumn
Layout.preferredWidth: statisticsTableCard.todayColWidth Layout.preferredWidth: statisticsTableCard.todayColWidth
font.bold: true font.weight: appBoldWeight
} }
Label { Label {
text: "最后签到时间" text: "最后签到时间"
visible: statisticsTableCard.showLastColumn visible: statisticsTableCard.showLastColumn
Layout.preferredWidth: statisticsTableCard.lastColWidth Layout.preferredWidth: statisticsTableCard.lastColWidth
font.bold: true font.weight: appBoldWeight
} }
Label { Label {
text: "平均置信度" text: "平均置信度"
visible: statisticsTableCard.showAverageColumn visible: statisticsTableCard.showAverageColumn
Layout.preferredWidth: statisticsTableCard.avgColWidth Layout.preferredWidth: statisticsTableCard.avgColWidth
font.bold: true font.weight: appBoldWeight
} }
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
} }
@@ -1609,7 +1610,7 @@ ApplicationWindow {
spacing: 12 spacing: 12
Label { text: model.studentId; Layout.preferredWidth: statisticsTableCard.studentColWidth; color: ink700 } Label { text: model.studentId; Layout.preferredWidth: statisticsTableCard.studentColWidth; color: ink700 }
Label { text: model.name; Layout.preferredWidth: statisticsTableCard.nameColWidth; color: ink900; font.bold: true } 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.totalCount; Layout.preferredWidth: statisticsTableCard.totalColWidth; color: ink700 }
Label { Label {
text: model.todayCount text: model.todayCount
@@ -1628,7 +1629,7 @@ ApplicationWindow {
visible: statisticsTableCard.showAverageColumn visible: statisticsTableCard.showAverageColumn
Layout.preferredWidth: statisticsTableCard.avgColWidth Layout.preferredWidth: statisticsTableCard.avgColWidth
color: tealColor color: tealColor
font.bold: true font.weight: appBoldWeight
} }
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
} }
@@ -1691,7 +1692,7 @@ ApplicationWindow {
Label { Label {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
text: "服务器地址" text: "服务器地址"
font.bold: true font.weight: appBoldWeight
color: ink700 color: ink700
} }
AppTextField { AppTextField {
@@ -1785,10 +1786,10 @@ ApplicationWindow {
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: 16 anchors.leftMargin: 16
anchors.rightMargin: 16 anchors.rightMargin: 16
Label { text: "名称"; Layout.preferredWidth: 220; font.bold: true } Label { text: "名称"; Layout.preferredWidth: 220; font.weight: appBoldWeight }
Label { text: "开始"; Layout.preferredWidth: 120; font.bold: true } Label { text: "开始"; Layout.preferredWidth: 120; font.weight: appBoldWeight }
Label { text: "结束"; Layout.preferredWidth: 120; font.bold: true } Label { text: "结束"; Layout.preferredWidth: 120; font.weight: appBoldWeight }
Label { text: "状态"; Layout.preferredWidth: 100; font.bold: true } Label { text: "状态"; Layout.preferredWidth: 100; font.weight: appBoldWeight }
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
} }
} }
@@ -1829,7 +1830,7 @@ ApplicationWindow {
text: model.enabled ? "启用" : "禁用" text: model.enabled ? "启用" : "禁用"
Layout.preferredWidth: 100 Layout.preferredWidth: 100
color: model.enabled ? successColor : goldColor color: model.enabled ? successColor : goldColor
font.bold: true font.weight: appBoldWeight
} }
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
SecondaryButton { text: "编辑"; onClicked: openTimeSlotDialog(index) } SecondaryButton { text: "编辑"; onClicked: openTimeSlotDialog(index) }
@@ -2163,7 +2164,7 @@ ApplicationWindow {
visible: !model.hasPhoto visible: !model.hasPhoto
text: "无照片" text: "无照片"
color: goldColor color: goldColor
font.bold: true font.weight: appBoldWeight
} }
} }
@@ -2171,7 +2172,7 @@ ApplicationWindow {
text: "ID: " + model.faceId text: "ID: " + model.faceId
color: ink900 color: ink900
font.pixelSize: compactLayout ? 15 : 16 font.pixelSize: compactLayout ? 15 : 16
font.bold: true font.weight: appBoldWeight
} }
AppTextField { AppTextField {
@@ -2192,7 +2193,7 @@ ApplicationWindow {
text: model.hasPhoto ? "照片已同步" : "仅检测到记录,无照片" text: model.hasPhoto ? "照片已同步" : "仅检测到记录,无照片"
color: model.hasPhoto ? successColor : goldColor color: model.hasPhoto ? successColor : goldColor
font.pixelSize: narrowLayout ? 11 : 12 font.pixelSize: narrowLayout ? 11 : 12
font.bold: true font.weight: appBoldWeight
} }
} }
@@ -2281,7 +2282,7 @@ ApplicationWindow {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: phoneLayout ? 124 : 108 Layout.preferredHeight: phoneLayout ? 188 : 164
radius: 8 radius: 8
color: "#f8fafb" color: "#f8fafb"
border.color: lineColor border.color: lineColor
@@ -2296,7 +2297,7 @@ ApplicationWindow {
text: "毕业设计说明" text: "毕业设计说明"
color: ink900 color: ink900
font.pixelSize: 16 font.pixelSize: 16
font.bold: true font.weight: appBoldWeight
} }
Label { Label {
@@ -2314,6 +2315,22 @@ ApplicationWindow {
color: ink500 color: ink500
font.pixelSize: 12 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
}
} }
} }
} }
+2 -3
View File
@@ -91,10 +91,9 @@ bool ApiClient::isConnectionError(QNetworkReply::NetworkError error) const
void ApiClient::emitNetworkError(const QString &prefix, QNetworkReply *reply) void ApiClient::emitNetworkError(const QString &prefix, QNetworkReply *reply)
{ {
const QString message = QStringLiteral("%1: %2").arg(prefix, reply->errorString()); emit networkError(prefix);
emit networkError(message);
if (isConnectionError(reply->error())) { if (isConnectionError(reply->error())) {
emit connectionUnavailable(message); emit connectionUnavailable(prefix);
} }
} }
+20 -10
View File
@@ -42,6 +42,18 @@ bool isSuccessStatus(const QString &status)
|| normalized == QStringLiteral("签到成功") || normalized == QStringLiteral("签到成功")
|| normalized == QStringLiteral("正常"); || normalized == QStringLiteral("正常");
} }
QString conciseStatusMessage(const QString &message)
{
if (message.contains(QStringLiteral("Error transferring"), Qt::CaseInsensitive)
|| message.contains(QStringLiteral("Connection refused"), Qt::CaseInsensitive)
|| message.contains(QStringLiteral("Host not found"), Qt::CaseInsensitive)
|| message.contains(QStringLiteral("Network unreachable"), Qt::CaseInsensitive)
|| message.contains(QStringLiteral("timed out"), Qt::CaseInsensitive)) {
return QStringLiteral("服务器未连接,稍后重试");
}
return message;
}
} }
AppController::AppController(QObject *parent) AppController::AppController(QObject *parent)
@@ -673,32 +685,30 @@ void AppController::mergeFaceItems()
void AppController::setStatusMessage(const QString &message) void AppController::setStatusMessage(const QString &message)
{ {
if (message == m_statusMessage) { const QString displayMessage = conciseStatusMessage(message);
if (displayMessage == m_statusMessage) {
return; return;
} }
m_statusMessage = message; m_statusMessage = displayMessage;
emit statusMessageChanged(); emit statusMessageChanged();
} }
void AppController::startReconnect(const QString &reason) void AppController::startReconnect(const QString &reason)
{ {
Q_UNUSED(reason)
if (!m_reconnecting) { if (!m_reconnecting) {
m_reconnecting = true; m_reconnecting = true;
m_reconnectAttempt = 0; m_reconnectAttempt = 0;
emit reconnectStateChanged(); emit reconnectStateChanged();
} }
const QString detail = reason.trimmed();
if (m_reconnectCheckInFlight || m_reconnectTimer.isActive()) { if (m_reconnectCheckInFlight || m_reconnectTimer.isActive()) {
setStatusMessage(detail.isEmpty() setStatusMessage(QStringLiteral("服务器未连接,稍后重试"));
? QStringLiteral("服务器连接失败,正在等待自动重连")
: QStringLiteral("服务器连接失败,正在等待自动重连: %1").arg(detail));
return; return;
} }
setStatusMessage(detail.isEmpty() setStatusMessage(QStringLiteral("服务器未连接,稍后重试"));
? QStringLiteral("服务器连接失败,%1 秒后自动重连").arg(ReconnectIntervalMs / 1000)
: QStringLiteral("服务器连接失败,%1 秒后自动重连: %2").arg(ReconnectIntervalMs / 1000).arg(detail));
scheduleReconnect(); scheduleReconnect();
} }
@@ -729,6 +739,6 @@ void AppController::runReconnectCheck()
m_reconnectCheckInFlight = true; m_reconnectCheckInFlight = true;
++m_reconnectAttempt; ++m_reconnectAttempt;
emit reconnectStateChanged(); emit reconnectStateChanged();
setStatusMessage(QStringLiteral("正在重连服务器,第 %1 次").arg(m_reconnectAttempt)); setStatusMessage(QStringLiteral("正在重连服务器"));
m_apiClient.checkHealth(); m_apiClient.checkHealth();
} }