基本完成

This commit is contained in:
2026-05-01 00:10:45 +08:00
parent ebc49d1ac6
commit 826cc4d56e
5 changed files with 140 additions and 74 deletions
+2
View File
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.16)
project(rewrite_qt VERSION 1.0.0 LANGUAGES CXX) project(rewrite_qt VERSION 1.0.0 LANGUAGES CXX)
set(REWRITE_QT_DISPLAY_NAME "人脸识别考勤系统")
set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml") set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml")
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -54,6 +55,7 @@ endif()
if(ANDROID) if(ANDROID)
set_target_properties(apprewrite_qt PROPERTIES set_target_properties(apprewrite_qt PROPERTIES
QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android" QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android"
QT_ANDROID_APP_NAME "${REWRITE_QT_DISPLAY_NAME}"
QT_ANDROID_APP_ICON "@mipmap/ic_launcher" QT_ANDROID_APP_ICON "@mipmap/ic_launcher"
) )
endif() endif()
+2 -1
View File
@@ -184,10 +184,11 @@ CMake 中 Android 相关配置:
```cmake ```cmake
QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android" QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android"
QT_ANDROID_APP_NAME "人脸识别考勤系统"
QT_ANDROID_APP_ICON "@mipmap/ic_launcher" QT_ANDROID_APP_ICON "@mipmap/ic_launcher"
``` ```
`QT_ANDROID_APP_ICON` 需要 Qt 6.9 或更高版本。当前 Qt 6.10 可正常使用。 `QT_ANDROID_APP_NAME` 用于设置 Android 桌面显示名,`QT_ANDROID_APP_ICON` 用于设置启动图标。
## 6. 功能说明 ## 6. 功能说明
+2
View File
@@ -0,0 +1,2 @@
android.overridePathCheck=true
android.useAndroidX=true
+1 -1
View File
@@ -1,6 +1,6 @@
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
Name=Face Attendance Name=人脸识别考勤系统
Exec=apprewrite_qt Exec=apprewrite_qt
Icon=rewrite_qt Icon=rewrite_qt
Terminal=false Terminal=false
+133 -72
View File
@@ -10,7 +10,7 @@ ApplicationWindow {
minimumWidth: 900 minimumWidth: 900
minimumHeight: 620 minimumHeight: 620
visible: true visible: true
title: "人脸识别考勤系统 - QML 管理端" title: "人脸识别考勤系统"
color: "#f6f7f9" color: "#f6f7f9"
readonly property color ink900: "#182230" readonly property color ink900: "#182230"
@@ -43,6 +43,7 @@ ApplicationWindow {
readonly property bool denseLayout: width < 1380 readonly property bool denseLayout: width < 1380
readonly property bool narrowLayout: width < 1280 readonly property bool narrowLayout: width < 1280
readonly property bool slimLayout: width < 1040 readonly property bool slimLayout: width < 1040
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: "Microsoft YaHei UI"
@@ -255,31 +256,33 @@ ApplicationWindow {
} }
component EmptyState: Item { component EmptyState: Item {
id: emptyState
property string title: "" property string title: ""
property string description: "" property string description: ""
property string badgeText: "" property string badgeText: ""
property color accent: accentColor property color accent: accentColor
readonly property bool tightState: phoneLayout || height < 190
implicitWidth: 320 implicitWidth: phoneLayout ? 280 : 320
implicitHeight: 220 implicitHeight: phoneLayout ? 154 : 220
width: Math.min(implicitWidth, parent ? Math.max(180, parent.width - 28) : implicitWidth)
height: Math.min(implicitHeight, parent ? Math.max(126, parent.height - 20) : implicitHeight)
z: 20 z: 20
Rectangle { Rectangle {
anchors.centerIn: parent anchors.fill: parent
width: Math.min(parent.width, 320)
height: 200
radius: 8 radius: 8
color: "#fffaf4" color: "#fffaf4"
border.color: "#e7ddd2" border.color: "#e7ddd2"
Column { Column {
anchors.centerIn: parent anchors.centerIn: parent
spacing: 12 spacing: emptyState.tightState ? 8 : 12
width: parent.width - 40 width: parent.width - 32
Rectangle { Rectangle {
width: 64 width: emptyState.tightState ? 52 : 64
height: 64 height: width
radius: 8 radius: 8
color: Qt.tint("#ffffff", Qt.rgba(accent.r, accent.g, accent.b, 0.16)) color: Qt.tint("#ffffff", Qt.rgba(accent.r, accent.g, accent.b, 0.16))
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
@@ -288,7 +291,7 @@ ApplicationWindow {
anchors.centerIn: parent anchors.centerIn: parent
text: badgeText text: badgeText
color: accent color: accent
font.pixelSize: 22 font.pixelSize: emptyState.tightState ? 18 : 22
font.bold: true font.bold: true
} }
} }
@@ -299,17 +302,18 @@ ApplicationWindow {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
color: ink900 color: ink900
font.pixelSize: 20 font.pixelSize: emptyState.tightState ? 18 : 20
font.bold: true font.bold: true
} }
Label { Label {
width: parent.width width: parent.width
visible: !emptyState.tightState || parent.width > 260
text: description text: description
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
color: ink500 color: ink500
font.pixelSize: 13 font.pixelSize: emptyState.tightState ? 12 : 13
} }
} }
} }
@@ -341,7 +345,7 @@ ApplicationWindow {
property string description: "" property string description: ""
property color accent: accentColor property color accent: accentColor
implicitHeight: descriptionLabel.visible ? 52 : 32 implicitHeight: descriptionLabel.visible ? (phoneLayout ? 64 : 52) : 32
implicitWidth: 300 implicitWidth: 300
RowLayout { RowLayout {
@@ -362,7 +366,7 @@ ApplicationWindow {
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
text: sectionHeader.title text: sectionHeader.title
font.pixelSize: 22 font.pixelSize: phoneLayout ? 20 : 22
font.bold: true font.bold: true
color: ink900 color: ink900
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
@@ -404,6 +408,29 @@ ApplicationWindow {
} }
} }
component MobileNavTabButton: TabButton {
id: control
implicitHeight: 42
padding: 0
background: Rectangle {
radius: 8
color: control.checked ? tealColor : panelStrong
border.width: 1
border.color: control.checked ? tealColor : lineColor
}
contentItem: Label {
text: control.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: control.checked ? "#f6fbfb" : ink700
font.pixelSize: phoneLayout ? 12 : 13
font.bold: true
elide: Text.ElideRight
}
}
function applyRecordFilters() { function applyRecordFilters() {
appController.applyFilters( appController.applyFilters(
classFilter.currentText, classFilter.currentText,
@@ -702,8 +729,11 @@ ApplicationWindow {
Label { Label {
anchors.centerIn: parent anchors.centerIn: parent
text: "管理端" width: parent.width - 18
font.pixelSize: compactLayout ? 24 : 28 text: "人脸识别考勤系统"
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
font.pixelSize: compactLayout ? 17 : 19
font.bold: true font.bold: true
color: "#ffffff" color: "#ffffff"
} }
@@ -835,7 +865,7 @@ ApplicationWindow {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: slimLayout ? 96 : 142 Layout.preferredHeight: phoneLayout ? 108 : (slimLayout ? 96 : 142)
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
@@ -852,10 +882,12 @@ ApplicationWindow {
spacing: 6 spacing: 6
Label { Label {
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.bold: true
color: ink900 color: ink900
elide: Text.ElideRight
} }
Label { Label {
@@ -922,15 +954,19 @@ ApplicationWindow {
id: navigationBar id: navigationBar
visible: slimLayout visible: slimLayout
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: visible ? implicitHeight : 0 Layout.preferredHeight: visible ? 42 : 0
spacing: 6
padding: 0
property real tabWidth: (width - spacing * 4) / 5
currentIndex: window.currentPageIndex currentIndex: window.currentPageIndex
onCurrentIndexChanged: window.switchPage(currentIndex) onCurrentIndexChanged: window.switchPage(currentIndex)
background: Item {}
TabButton { text: "考勤记录" } MobileNavTabButton { width: navigationBar.tabWidth; text: "考勤记录" }
TabButton { text: "统计分析" } MobileNavTabButton { width: navigationBar.tabWidth; text: "统计分析" }
TabButton { text: "配置中心" } MobileNavTabButton { width: navigationBar.tabWidth; text: "配置中心" }
TabButton { text: "人脸管理" } MobileNavTabButton { width: navigationBar.tabWidth; text: "人脸管理" }
TabButton { text: "关于" } MobileNavTabButton { width: navigationBar.tabWidth; text: "关于" }
} }
StackLayout { StackLayout {
@@ -1069,7 +1105,7 @@ ApplicationWindow {
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
Layout.preferredHeight: slimLayout ? 174 : 92 Layout.preferredHeight: phoneLayout ? 236 : (slimLayout ? 174 : 92)
clip: true clip: true
GridLayout { GridLayout {
@@ -1080,8 +1116,8 @@ ApplicationWindow {
rowSpacing: 12 rowSpacing: 12
Column { Column {
Layout.preferredWidth: 150 Layout.fillWidth: true
width: 150 Layout.preferredWidth: phoneLayout ? 0 : 150
spacing: 6 spacing: 6
Label { text: "班级"; color: ink700; font.bold: true; font.pixelSize: 12 } Label { text: "班级"; color: ink700; font.bold: true; font.pixelSize: 12 }
AppComboBox { AppComboBox {
@@ -1093,8 +1129,8 @@ ApplicationWindow {
} }
Column { Column {
Layout.preferredWidth: 160 Layout.fillWidth: true
width: 160 Layout.preferredWidth: phoneLayout ? 0 : 160
spacing: 6 spacing: 6
Label { text: "学号"; color: ink700; font.bold: true; font.pixelSize: 12 } Label { text: "学号"; color: ink700; font.bold: true; font.pixelSize: 12 }
AppComboBox { AppComboBox {
@@ -1106,8 +1142,8 @@ ApplicationWindow {
} }
Column { Column {
Layout.preferredWidth: 190 Layout.fillWidth: true
width: 190 Layout.preferredWidth: phoneLayout ? 0 : 190
spacing: 6 spacing: 6
Label { text: "姓名"; color: ink700; font.bold: true; font.pixelSize: 12 } Label { text: "姓名"; color: ink700; font.bold: true; font.pixelSize: 12 }
AppTextField { AppTextField {
@@ -1119,8 +1155,8 @@ ApplicationWindow {
} }
Column { Column {
Layout.preferredWidth: 160 Layout.fillWidth: true
width: 160 Layout.preferredWidth: phoneLayout ? 0 : 160
spacing: 6 spacing: 6
Label { text: "日期"; color: ink700; font.bold: true; font.pixelSize: 12 } Label { text: "日期"; color: ink700; font.bold: true; font.pixelSize: 12 }
AppComboBox { AppComboBox {
@@ -1133,8 +1169,9 @@ ApplicationWindow {
} }
Column { Column {
Layout.preferredWidth: 150 Layout.fillWidth: true
width: dateFilter.currentIndex === 4 ? 150 : 0 Layout.preferredWidth: phoneLayout ? 0 : 150
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
@@ -1148,9 +1185,11 @@ ApplicationWindow {
} }
} }
Row { Flow {
Layout.columnSpan: slimLayout ? 2 : 1
Layout.fillWidth: true
Layout.alignment: Qt.AlignBottom Layout.alignment: Qt.AlignBottom
height: 42 height: implicitHeight
spacing: 8 spacing: 8
SecondaryButton { SecondaryButton {
@@ -1180,12 +1219,12 @@ ApplicationWindow {
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
property real timeColWidth: Math.max(168, Math.min(220, width * 0.21)) property real timeColWidth: phoneLayout ? 112 : Math.max(168, Math.min(220, width * 0.21))
property real classColWidth: Math.max(88, Math.min(124, width * 0.11)) property real classColWidth: phoneLayout ? 0 : Math.max(88, Math.min(124, width * 0.11))
property real studentColWidth: Math.max(86, Math.min(116, width * 0.10)) property real studentColWidth: phoneLayout ? 64 : Math.max(86, Math.min(116, width * 0.10))
property real nameColWidth: Math.max(118, Math.min(158, width * 0.14)) property real nameColWidth: phoneLayout ? 88 : Math.max(118, Math.min(158, width * 0.14))
property real confidenceColWidth: Math.max(96, Math.min(126, width * 0.11)) property real confidenceColWidth: phoneLayout ? 0 : Math.max(96, Math.min(126, width * 0.11))
property real statusColWidth: Math.max(84, Math.min(102, width * 0.09)) property real statusColWidth: phoneLayout ? 64 : Math.max(84, Math.min(102, width * 0.09))
property bool showClassColumn: !denseLayout property bool showClassColumn: !denseLayout
property bool showConfidenceColumn: !narrowLayout property bool showConfidenceColumn: !narrowLayout
@@ -1325,7 +1364,7 @@ ApplicationWindow {
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
Layout.preferredHeight: slimLayout ? 150 : 78 Layout.preferredHeight: phoneLayout ? 186 : (slimLayout ? 150 : 78)
clip: true clip: true
GridLayout { GridLayout {
@@ -1338,7 +1377,8 @@ ApplicationWindow {
AppComboBox { AppComboBox {
id: quickStatisticsFilter id: quickStatisticsFilter
Layout.preferredWidth: 150 Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 0 : 150
model: ["今天", "最近7天", "最近30天", "本月"] model: ["今天", "最近7天", "最近30天", "本月"]
currentIndex: 2 currentIndex: 2
onCurrentTextChanged: { onCurrentTextChanged: {
@@ -1349,14 +1389,16 @@ ApplicationWindow {
AppTextField { AppTextField {
id: statsStartField id: statsStartField
Layout.preferredWidth: 150 Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 0 : 150
placeholderText: "开始日期" placeholderText: "开始日期"
onEditingFinished: appController.updateStatisticsRange(text, statsEndField.text) onEditingFinished: appController.updateStatisticsRange(text, statsEndField.text)
} }
AppTextField { AppTextField {
id: statsEndField id: statsEndField
Layout.preferredWidth: 150 Layout.fillWidth: true
Layout.preferredWidth: phoneLayout ? 0 : 150
placeholderText: "结束日期" placeholderText: "结束日期"
onEditingFinished: appController.updateStatisticsRange(statsStartField.text, text) onEditingFinished: appController.updateStatisticsRange(statsStartField.text, text)
} }
@@ -1375,13 +1417,13 @@ ApplicationWindow {
GridLayout { GridLayout {
Layout.fillWidth: true Layout.fillWidth: true
columns: slimLayout ? 3 : (compactLayout ? 1 : 3) columns: phoneLayout ? 1 : (slimLayout ? 3 : (compactLayout ? 1 : 3))
rowSpacing: 10 rowSpacing: 10
columnSpacing: 14 columnSpacing: 14
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: slimLayout ? 74 : 100 Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100)
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
@@ -1411,7 +1453,7 @@ ApplicationWindow {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: slimLayout ? 74 : 100 Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100)
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
@@ -1441,7 +1483,7 @@ ApplicationWindow {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: slimLayout ? 74 : 100 Layout.preferredHeight: phoneLayout ? 68 : (slimLayout ? 74 : 100)
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
@@ -1460,6 +1502,7 @@ ApplicationWindow {
font.pixelSize: slimLayout ? 14 : (compactLayout ? 18 : 20) font.pixelSize: slimLayout ? 14 : (compactLayout ? 18 : 20)
font.bold: true font.bold: true
color: goldColor color: goldColor
elide: Text.ElideRight
} }
} }
@@ -1483,14 +1526,15 @@ ApplicationWindow {
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
property real studentColWidth: Math.max(90, Math.min(128, width * 0.12)) property real studentColWidth: phoneLayout ? 68 : Math.max(90, Math.min(128, width * 0.12))
property real nameColWidth: Math.max(128, Math.min(188, width * 0.17)) property real nameColWidth: phoneLayout ? 96 : Math.max(128, Math.min(188, width * 0.17))
property real totalColWidth: Math.max(96, Math.min(124, width * 0.12)) property real totalColWidth: phoneLayout ? 92 : Math.max(96, Math.min(124, width * 0.12))
property real todayColWidth: Math.max(92, Math.min(110, width * 0.10)) property real todayColWidth: Math.max(92, Math.min(110, width * 0.10))
property real lastColWidth: Math.max(180, Math.min(240, width * 0.20)) property real lastColWidth: Math.max(180, Math.min(240, width * 0.20))
property real avgColWidth: Math.max(98, Math.min(126, width * 0.11)) property real avgColWidth: Math.max(98, Math.min(126, width * 0.11))
property bool showTodayColumn: !denseLayout property bool showTodayColumn: !denseLayout
property bool showLastColumn: !narrowLayout property bool showLastColumn: !narrowLayout
property bool showAverageColumn: !phoneLayout
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
@@ -1524,7 +1568,12 @@ ApplicationWindow {
Layout.preferredWidth: statisticsTableCard.lastColWidth Layout.preferredWidth: statisticsTableCard.lastColWidth
font.bold: true font.bold: true
} }
Label { text: "平均置信度"; Layout.preferredWidth: statisticsTableCard.avgColWidth; font.bold: true } Label {
text: "平均置信度"
visible: statisticsTableCard.showAverageColumn
Layout.preferredWidth: statisticsTableCard.avgColWidth
font.bold: true
}
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
} }
} }
@@ -1574,7 +1623,13 @@ ApplicationWindow {
Layout.preferredWidth: statisticsTableCard.lastColWidth Layout.preferredWidth: statisticsTableCard.lastColWidth
color: ink700 color: ink700
} }
Label { text: model.averageConfidenceText; Layout.preferredWidth: statisticsTableCard.avgColWidth; color: tealColor; font.bold: true } Label {
text: model.averageConfidenceText
visible: statisticsTableCard.showAverageColumn
Layout.preferredWidth: statisticsTableCard.avgColWidth
color: tealColor
font.bold: true
}
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
} }
} }
@@ -1622,7 +1677,7 @@ ApplicationWindow {
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
Layout.preferredHeight: slimLayout ? 174 : 88 Layout.preferredHeight: phoneLayout ? 218 : (slimLayout ? 174 : 88)
clip: true clip: true
GridLayout { GridLayout {
@@ -1648,11 +1703,13 @@ ApplicationWindow {
} }
SecondaryButton { SecondaryButton {
Layout.fillWidth: phoneLayout
text: "保存地址" text: "保存地址"
onClicked: appController.serverUrl = serverUrlField.text onClicked: appController.serverUrl = serverUrlField.text
} }
PrimaryButton { PrimaryButton {
Layout.fillWidth: phoneLayout
text: "测试连接" text: "测试连接"
fillColor: tealColor fillColor: tealColor
onClicked: appController.testConnection() onClicked: appController.testConnection()
@@ -2019,13 +2076,15 @@ ApplicationWindow {
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
Layout.preferredHeight: slimLayout ? 132 : 88 Layout.preferredHeight: phoneLayout ? 170 : (slimLayout ? 132 : 88)
clip: true clip: true
RowLayout { GridLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: slimLayout ? 12 : 18 anchors.margins: slimLayout ? 12 : 18
spacing: 12 columns: phoneLayout ? 1 : 2
rowSpacing: 12
columnSpacing: 12
SectionHeader { SectionHeader {
Layout.fillWidth: true Layout.fillWidth: true
@@ -2034,8 +2093,9 @@ ApplicationWindow {
accent: goldColor accent: goldColor
} }
Row { Flow {
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.fillWidth: phoneLayout
Layout.alignment: phoneLayout ? Qt.AlignLeft : (Qt.AlignRight | Qt.AlignVCenter)
spacing: 8 spacing: 8
SecondaryButton { text: "刷新"; onClicked: appController.refreshFaces() } SecondaryButton { text: "刷新"; onClicked: appController.refreshFaces() }
@@ -2059,14 +2119,14 @@ ApplicationWindow {
anchors.fill: parent anchors.fill: parent
anchors.margins: 16 anchors.margins: 16
clip: true clip: true
property int cardColumns: Math.max(1, Math.floor(width / (compactLayout ? 236 : 260))) property int cardColumns: phoneLayout ? 1 : Math.max(1, Math.floor(width / (compactLayout ? 236 : 260)))
cellWidth: Math.max(240, Math.floor(width / cardColumns)) cellWidth: Math.max(phoneLayout ? 280 : 240, Math.floor(width / cardColumns))
cellHeight: 332 cellHeight: phoneLayout ? 392 : 352
model: appController.faceModel model: appController.faceModel
delegate: Rectangle { delegate: Rectangle {
width: Math.min(244, GridView.view.cellWidth - 18) width: Math.min(phoneLayout ? 300 : 244, GridView.view.cellWidth - 18)
height: 308 height: phoneLayout ? 368 : 328
property bool hovered: faceHoverHandler.hovered property bool hovered: faceHoverHandler.hovered
radius: 8 radius: 8
color: hovered ? "#fff6ef" : "#ffffff" color: hovered ? "#fff6ef" : "#ffffff"
@@ -2085,7 +2145,7 @@ ApplicationWindow {
Rectangle { Rectangle {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: compactLayout ? 152 : 168 Layout.preferredHeight: phoneLayout ? 140 : (compactLayout ? 152 : 168)
radius: 16 radius: 16
color: model.hasPhoto ? "#eef3f5" : goldSoft color: model.hasPhoto ? "#eef3f5" : goldSoft
border.color: model.hasPhoto ? "#dbe4ea" : "#eadbb0" border.color: model.hasPhoto ? "#dbe4ea" : "#eadbb0"
@@ -2179,15 +2239,16 @@ ApplicationWindow {
radius: 8 radius: 8
color: panelStrong color: panelStrong
border.color: lineColor border.color: lineColor
clip: true
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: 24 anchors.margins: phoneLayout ? 16 : 24
spacing: 18 spacing: phoneLayout ? 12 : 18
SectionHeader { SectionHeader {
Layout.fillWidth: true Layout.fillWidth: true
title: "人脸识别考勤系统管理端" title: "人脸识别考勤系统"
description: "用于连接考勤服务端,集中查看签到记录、统计考勤情况、维护签到时段、管理名称映射与人脸照片绑定。" description: "用于连接考勤服务端,集中查看签到记录、统计考勤情况、维护签到时段、管理名称映射与人脸照片绑定。"
accent: tealColor accent: tealColor
} }
@@ -2220,7 +2281,7 @@ ApplicationWindow {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 92 Layout.preferredHeight: phoneLayout ? 124 : 108
radius: 8 radius: 8
color: "#f8fafb" color: "#f8fafb"
border.color: lineColor border.color: lineColor