初始化
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
#ifndef ATTENDANCERECORD_H
|
||||
#define ATTENDANCERECORD_H
|
||||
|
||||
#include <QString>
|
||||
#include <QDateTime>
|
||||
|
||||
/**
|
||||
* @brief 考勤记录数据模型
|
||||
*
|
||||
* 对应CSV文件格式:Timestamp,Student_ID,Name,Confidence,Status,Class_ID
|
||||
*/
|
||||
class AttendanceRecord
|
||||
{
|
||||
public:
|
||||
AttendanceRecord();
|
||||
AttendanceRecord(const QString ×tamp, const QString &studentId,
|
||||
const QString &name, float confidence, const QString &status,
|
||||
const QString &classId = "0");
|
||||
|
||||
// Getters
|
||||
QString getTimestamp() const { return m_timestamp; }
|
||||
QString getStudentId() const { return m_studentId; }
|
||||
QString getName() const { return m_name; }
|
||||
float getConfidence() const { return m_confidence; }
|
||||
QString getStatus() const { return m_status; }
|
||||
QString getClassId() const { return m_classId; }
|
||||
QDateTime getDateTime() const { return m_dateTime; }
|
||||
|
||||
// Setters
|
||||
void setTimestamp(const QString ×tamp);
|
||||
void setStudentId(const QString &studentId) { m_studentId = studentId; }
|
||||
void setName(const QString &name) { m_name = name; }
|
||||
void setConfidence(float confidence) { m_confidence = confidence; }
|
||||
void setStatus(const QString &status) { m_status = status; }
|
||||
void setClassId(const QString &classId) { m_classId = classId; }
|
||||
|
||||
// 从CSV行解析
|
||||
static AttendanceRecord fromCsvLine(const QString &line);
|
||||
|
||||
// 转换为显示字符串
|
||||
QString toString() const;
|
||||
|
||||
private:
|
||||
QString m_timestamp; // 时间戳字符串
|
||||
QString m_studentId; // 学号
|
||||
QString m_name; // 姓名
|
||||
float m_confidence; // 置信度 (0.0-1.0)
|
||||
QString m_status; // 状态 (success/fail)
|
||||
QString m_classId; // 班级编号
|
||||
QDateTime m_dateTime; // 解析后的日期时间对象
|
||||
};
|
||||
|
||||
#endif // ATTENDANCERECORD_H
|
||||
@@ -0,0 +1,98 @@
|
||||
#ifndef FACEMANAGEDIALOG_H
|
||||
#define FACEMANAGEDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QGridLayout>
|
||||
#include <QScrollArea>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QMap>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
|
||||
/**
|
||||
* @brief 人脸照片项组件
|
||||
* 显示单个人脸照片及其学号映射
|
||||
*/
|
||||
class FacePhotoItem : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FacePhotoItem(const QString &faceId, bool hasPhoto = true, QWidget *parent = nullptr);
|
||||
|
||||
QString getFaceId() const { return m_faceId; }
|
||||
QString getStudentId() const { return m_studentIdEdit->text().trimmed(); }
|
||||
void setStudentId(const QString &studentId) { m_studentIdEdit->setText(studentId); }
|
||||
void setPhoto(const QPixmap &pixmap);
|
||||
bool hasPhoto() const { return m_hasPhoto; }
|
||||
|
||||
signals:
|
||||
void studentIdChanged(const QString &faceId, const QString &studentId);
|
||||
void deleteRequested(const QString &faceId);
|
||||
|
||||
private:
|
||||
QString m_faceId;
|
||||
bool m_hasPhoto;
|
||||
QLabel *m_photoLabel;
|
||||
QLabel *m_idLabel;
|
||||
QLineEdit *m_studentIdEdit;
|
||||
QPushButton *m_deleteButton;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 人脸管理对话框
|
||||
* 显示K210上传的人脸照片,允许配置学号映射
|
||||
*/
|
||||
class FaceManageDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FaceManageDialog(QWidget *parent = nullptr);
|
||||
~FaceManageDialog();
|
||||
|
||||
void setServerUrl(const QString &url) { m_serverUrl = url; }
|
||||
|
||||
signals:
|
||||
void mappingSaved();
|
||||
|
||||
public slots:
|
||||
void refreshPhotos();
|
||||
|
||||
private slots:
|
||||
void onPhotoListReceived(QNetworkReply *reply);
|
||||
void onPhotoReceived(QNetworkReply *reply);
|
||||
void onMappingReceived(QNetworkReply *reply);
|
||||
void onMappingSaved(QNetworkReply *reply);
|
||||
void onAllStudentIdsReceived(QNetworkReply *reply);
|
||||
void onFaceDeleted(QNetworkReply *reply);
|
||||
void saveMapping();
|
||||
void onDeleteRequested(const QString &studentId);
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
void fetchPhotoList();
|
||||
void fetchPhoto(const QString &faceId);
|
||||
void fetchMapping();
|
||||
void fetchAllStudentIds();
|
||||
void uploadMapping();
|
||||
void deleteFace(const QString &studentId, bool deleteRecords);
|
||||
void clearPhotoItems();
|
||||
|
||||
QString m_serverUrl;
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QScrollArea *m_scrollArea;
|
||||
QWidget *m_contentWidget;
|
||||
QGridLayout *m_gridLayout;
|
||||
QPushButton *m_refreshButton;
|
||||
QPushButton *m_saveButton;
|
||||
QLabel *m_statusLabel;
|
||||
|
||||
QMap<QString, FacePhotoItem*> m_photoItems; // faceId -> item
|
||||
QMap<QString, QString> m_idMapping; // faceId -> studentId
|
||||
int m_pendingPhotoCount;
|
||||
};
|
||||
|
||||
#endif // FACEMANAGEDIALOG_H
|
||||
@@ -0,0 +1,135 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QTableWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
#include <QDateEdit>
|
||||
#include <QVector>
|
||||
#include <QEvent>
|
||||
#include <QDialog>
|
||||
#include "AttendanceRecord.h"
|
||||
#include "NetworkApiManager.h"
|
||||
|
||||
class MappingConfigDialog;
|
||||
|
||||
/**
|
||||
* @brief 对话框位置限制过滤器
|
||||
* 限制对话框只能在父窗口范围内移动
|
||||
*/
|
||||
class DialogPositionFilter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DialogPositionFilter(QWidget *parent = nullptr) : QObject(parent) {}
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 考勤记录管理端主窗口(网络版)
|
||||
*
|
||||
* 功能:
|
||||
* - 通过HTTP API从服务器获取数据
|
||||
* - 显示考勤记录表格
|
||||
* - 自动刷新数据(30秒)
|
||||
* - 按学号、姓名、日期筛选
|
||||
* - 统计信息显示
|
||||
* - 服务器配置管理
|
||||
*/
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
private slots:
|
||||
// 数据操作
|
||||
void loadAttendanceData(); // 从服务器加载考勤数据
|
||||
void refreshData(); // 刷新数据
|
||||
void filterData(); // 筛选数据
|
||||
void exportToExcel(); // 导出为CSV
|
||||
void exportSelectedRecords(); // 导出选中的记录
|
||||
void clearFilter(); // 清除筛选条件
|
||||
void deleteSelectedRecord(); // 删除选中的记录
|
||||
void deleteSelectedRecords(); // 批量删除选中的记录
|
||||
void toggleSelectAll(); // 全选/取消全选
|
||||
void unselectAll(); // 取消选中
|
||||
|
||||
// 管理功能
|
||||
void onClearAllRecords(); // 清空所有签到记录(调用API)
|
||||
void onOpenSettings(); // 打开时间段设置
|
||||
void onOpenMappingConfig(); // 打开映射配置
|
||||
void onOpenFaceManage(); // 打开人脸管理
|
||||
void onShowStatistics(); // 显示统计报表
|
||||
void onAbout(); // 关于对话框
|
||||
|
||||
// 网络响应处理
|
||||
void onRecordsFetched(const QVector<AttendanceRecord> &records);
|
||||
void onStatisticsFetched(int total, int today, const QString &serverTime);
|
||||
void onRecordsCleared(bool success);
|
||||
void onRecordDeleted(bool success); // 新增:删除记录响应
|
||||
void onNetworkError(const QString &error);
|
||||
void onMappingConfigFetched(const QMap<QString, QString> &studentNameMap,
|
||||
const QMap<QString, QString> &classNameMap);
|
||||
void onMappingConfigUploaded(bool success);
|
||||
|
||||
private:
|
||||
void setupUI(); // 设置UI界面
|
||||
void setupMenuBar(); // 设置菜单栏
|
||||
void setupConnections(); // 设置信号槽连接
|
||||
void updateTable(const QVector<AttendanceRecord> &records); // 更新表格
|
||||
void updateStatistics(int total, int today); // 更新统计信息
|
||||
void showConnectionStatus(bool connected); // 显示连接状态
|
||||
|
||||
// UI组件
|
||||
QTableWidget *m_tableWidget;
|
||||
QPushButton *m_refreshButton;
|
||||
QPushButton *m_exportButton;
|
||||
QPushButton *m_exportSelectedButton; // 导出选中
|
||||
QPushButton *m_deleteSelectedButton; // 删除选中
|
||||
QPushButton *m_clearFilterButton;
|
||||
QPushButton *m_settingsButton;
|
||||
QPushButton *m_statsButton;
|
||||
QPushButton *m_clearRecordsButton;
|
||||
QCheckBox *m_selectAllCheckBox; // 全选复选框
|
||||
QPushButton *m_unselectAllButton; // 取消选中按钮
|
||||
QLabel *m_totalLabel;
|
||||
QLabel *m_todayLabel;
|
||||
QLabel *m_statusLabel;
|
||||
QLabel *m_connectionLabel;
|
||||
QLabel *m_selectedCountLabel; // 显示选中数量
|
||||
QComboBox *m_classFilterCombo; // 班级筛选下拉框
|
||||
QComboBox *m_studentFilterCombo;
|
||||
QLineEdit *m_nameFilterEdit;
|
||||
QComboBox *m_dateFilterCombo;
|
||||
QDateEdit *m_specificDateEdit; // 特定日期选择器
|
||||
QCheckBox *m_enableSpecificDateCheckBox; // 启用特定日期筛选
|
||||
|
||||
// 数据
|
||||
QVector<AttendanceRecord> m_allRecords; // 所有记录
|
||||
QVector<AttendanceRecord> m_filteredRecords; // 筛选后的记录
|
||||
QSet<QString> m_selectedRecordKeys; // 保存选中记录的唯一键(timestamp+studentId)
|
||||
QMap<QString, QString> m_studentNameMap; // 学号 -> 真实姓名 映射
|
||||
QMap<QString, QString> m_classNameMap; // 班级编号 -> 班级名称 映射
|
||||
|
||||
// 辅助函数
|
||||
QString getMappedStudentName(const QString &studentId, const QString &originalName) const;
|
||||
QString getMappedClassName(const QString &classId) const;
|
||||
void loadMappingConfigFromSettings();
|
||||
void saveMappingConfigToSettings();
|
||||
|
||||
// 网络
|
||||
NetworkApiManager *m_apiManager; // 网络API管理器
|
||||
QTimer *m_autoRefreshTimer; // 自动刷新定时器
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
@@ -0,0 +1,89 @@
|
||||
#ifndef MAPPINGCONFIGDIALOG_H
|
||||
#define MAPPINGCONFIGDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTableWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
#include <QMap>
|
||||
#include <QTabWidget>
|
||||
#include <QLabel>
|
||||
|
||||
/**
|
||||
* @brief 映射配置对话框
|
||||
*
|
||||
* 用于配置:
|
||||
* 1. 学号 -> 真实姓名 的映射
|
||||
* 2. 班级编号 -> 真实班级名称 的映射
|
||||
*
|
||||
* 特性:
|
||||
* - 双击单元格可直接编辑(类似Excel)
|
||||
* - 自动从服务器加载已有映射
|
||||
* - 一键保存到服务器
|
||||
*/
|
||||
class MappingConfigDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MappingConfigDialog(QWidget *parent = nullptr);
|
||||
~MappingConfigDialog();
|
||||
|
||||
// 获取映射数据
|
||||
QMap<QString, QString> getStudentNameMap() const { return m_studentNameMap; }
|
||||
QMap<QString, QString> getClassNameMap() const { return m_classNameMap; }
|
||||
|
||||
// 设置映射数据(用于从服务器加载后更新)
|
||||
void setStudentNameMap(const QMap<QString, QString> &map);
|
||||
void setClassNameMap(const QMap<QString, QString> &map);
|
||||
|
||||
// 从表格收集数据到映射
|
||||
void collectDataFromTables();
|
||||
|
||||
// 设置状态文本
|
||||
void setStatus(const QString &status);
|
||||
|
||||
signals:
|
||||
void mappingChanged(); // 映射数据已修改,需要保存
|
||||
void requestSyncFromServer(); // 请求从服务器加载数据
|
||||
|
||||
private slots:
|
||||
void onAddStudent();
|
||||
void onDeleteStudent();
|
||||
void onAddClass();
|
||||
void onDeleteClass();
|
||||
void onSave();
|
||||
void onSyncToServer();
|
||||
void onSyncFromServer();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
void updateStudentTable();
|
||||
void updateClassTable();
|
||||
|
||||
// UI组件
|
||||
QTabWidget *m_tabWidget;
|
||||
QLabel *m_statusLabel;
|
||||
|
||||
// 学号-姓名映射表
|
||||
QTableWidget *m_studentTable;
|
||||
QPushButton *m_addStudentBtn;
|
||||
QPushButton *m_deleteStudentBtn;
|
||||
|
||||
// 班级编号-名称映射表
|
||||
QTableWidget *m_classTable;
|
||||
QPushButton *m_addClassBtn;
|
||||
QPushButton *m_deleteClassBtn;
|
||||
|
||||
// 底部按钮
|
||||
QPushButton *m_syncFromServerBtn;
|
||||
QPushButton *m_syncToServerBtn;
|
||||
QPushButton *m_saveBtn;
|
||||
QPushButton *m_cancelBtn;
|
||||
|
||||
// 数据
|
||||
QMap<QString, QString> m_studentNameMap; // 学号 -> 姓名
|
||||
QMap<QString, QString> m_classNameMap; // 班级编号 -> 班级名称
|
||||
};
|
||||
|
||||
#endif // MAPPINGCONFIGDIALOG_H
|
||||
@@ -0,0 +1,89 @@
|
||||
#ifndef NETWORKAPIMANAGER_H
|
||||
#define NETWORKAPIMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QVector>
|
||||
#include "AttendanceRecord.h"
|
||||
#include "TimeSlotConfig.h"
|
||||
|
||||
/**
|
||||
* @brief 网络API管理类
|
||||
*
|
||||
* 负责与服务器端HTTP API通信(支持AES-128-CBC加密)
|
||||
* API端点:http://server_ip:8082/api/
|
||||
*/
|
||||
class NetworkApiManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NetworkApiManager(QObject *parent = nullptr);
|
||||
~NetworkApiManager();
|
||||
|
||||
// 设置服务器地址
|
||||
void setServerUrl(const QString &url);
|
||||
QString getServerUrl() const { return m_serverUrl; }
|
||||
|
||||
// 设置是否启用加密通信
|
||||
void setEncryptionEnabled(bool enabled) { m_encryptionEnabled = enabled; }
|
||||
bool isEncryptionEnabled() const { return m_encryptionEnabled; }
|
||||
|
||||
// API调用
|
||||
void fetchRecords(int limit = 100, int offset = 0);
|
||||
void fetchStatistics();
|
||||
void clearAllRecords();
|
||||
void deleteRecord(const QString ×tamp, const QString &studentId);
|
||||
void uploadTimeSlots(const QVector<TimeSlot> &timeSlots);
|
||||
void checkHealth();
|
||||
|
||||
// 映射配置API
|
||||
void uploadMappingConfig(const QMap<QString, QString> &studentNameMap,
|
||||
const QMap<QString, QString> &classNameMap);
|
||||
void fetchMappingConfig();
|
||||
|
||||
// 人脸照片管理API
|
||||
void fetchFacePhotos();
|
||||
void fetchFacePhoto(const QString &faceId);
|
||||
void uploadFaceIdMapping(const QMap<QString, QString> &mapping);
|
||||
void fetchFaceIdMapping();
|
||||
|
||||
signals:
|
||||
// 响应信号
|
||||
void recordsFetched(const QVector<AttendanceRecord> &records);
|
||||
void statisticsFetched(int total, int today, const QString &serverTime);
|
||||
void recordsCleared(bool success);
|
||||
void recordDeleted(bool success);
|
||||
void timeSlotsUploaded(bool success);
|
||||
void healthChecked(bool healthy);
|
||||
void errorOccurred(const QString &error);
|
||||
|
||||
// 映射配置信号
|
||||
void mappingConfigUploaded(bool success);
|
||||
void mappingConfigFetched(const QMap<QString, QString> &studentNameMap,
|
||||
const QMap<QString, QString> &classNameMap);
|
||||
|
||||
// 人脸照片管理信号
|
||||
void facePhotosFetched(const QJsonArray &photos);
|
||||
void facePhotoFetched(const QString &faceId, const QByteArray &photoData);
|
||||
void faceIdMappingUploaded(bool success);
|
||||
void faceIdMappingFetched(const QMap<QString, QString> &mapping);
|
||||
|
||||
private slots:
|
||||
void onReplyFinished(QNetworkReply *reply);
|
||||
|
||||
private:
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QString m_serverUrl;
|
||||
bool m_encryptionEnabled; // 是否启用加密通信
|
||||
|
||||
QString buildApiUrl(const QString &endpoint, const QMap<QString, QString> ¶ms = QMap<QString, QString>());
|
||||
QVector<AttendanceRecord> parseRecordsJson(const QByteArray &json);
|
||||
|
||||
// AES加密/解密功能
|
||||
QByteArray aesDecrypt(const QByteArray &encryptedData);
|
||||
QByteArray removePKCS7Padding(const QByteArray &data);
|
||||
};
|
||||
|
||||
#endif // NETWORKAPIMANAGER_H
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef SERVERCONFIGDIALOG_H
|
||||
#define SERVERCONFIGDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
|
||||
/**
|
||||
* @brief 服务器配置对话框
|
||||
*/
|
||||
class ServerConfigDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ServerConfigDialog(const QString ¤tUrl, QWidget *parent = nullptr);
|
||||
QString getServerUrl() const;
|
||||
|
||||
private slots:
|
||||
void onTestConnection();
|
||||
void onSave();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
QLineEdit *m_urlEdit;
|
||||
QPushButton *m_testButton;
|
||||
QLabel *m_statusLabel;
|
||||
QString m_serverUrl;
|
||||
};
|
||||
|
||||
#endif // SERVERCONFIGDIALOG_H
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef SETTINGSDIALOG_H
|
||||
#define SETTINGSDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTableWidget>
|
||||
#include <QPushButton>
|
||||
#include <QTimeEdit>
|
||||
#include <QLineEdit>
|
||||
#include <QCheckBox>
|
||||
#include "TimeSlotConfig.h"
|
||||
#include "NetworkApiManager.h"
|
||||
|
||||
/**
|
||||
* @brief 设置对话框 - 管理签到时间段
|
||||
*/
|
||||
class SettingsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SettingsDialog(QWidget *parent = nullptr);
|
||||
~SettingsDialog();
|
||||
|
||||
private slots:
|
||||
void onAddTimeSlot();
|
||||
void onEditTimeSlot();
|
||||
void onDeleteTimeSlot();
|
||||
void onResetToDefault();
|
||||
void onSave();
|
||||
void onCancel();
|
||||
void onTimeSlotsUploaded(bool success); // 新增:处理上传结果
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
void loadTimeSlots();
|
||||
void saveTimeSlots();
|
||||
void refreshTimeSlots(); // 新增:只刷新表格显示,不重新加载数据
|
||||
|
||||
// UI组件
|
||||
QTableWidget *m_tableWidget;
|
||||
QPushButton *m_addButton;
|
||||
QPushButton *m_editButton;
|
||||
QPushButton *m_deleteButton;
|
||||
QPushButton *m_resetButton;
|
||||
QPushButton *m_saveButton;
|
||||
QPushButton *m_cancelButton;
|
||||
|
||||
// 临时存储
|
||||
QVector<TimeSlot> m_tempTimeSlots;
|
||||
|
||||
// 网络API管理器
|
||||
NetworkApiManager *m_apiManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 时间段编辑对话框
|
||||
*/
|
||||
class TimeSlotEditDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TimeSlotEditDialog(QWidget *parent = nullptr, const TimeSlot *slot = nullptr);
|
||||
TimeSlot getTimeSlot() const;
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
|
||||
QLineEdit *m_nameEdit;
|
||||
QTimeEdit *m_startTimeEdit;
|
||||
QTimeEdit *m_endTimeEdit;
|
||||
QCheckBox *m_enabledCheckBox;
|
||||
};
|
||||
|
||||
#endif // SETTINGSDIALOG_H
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef STATISTICSDIALOG_H
|
||||
#define STATISTICSDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTableWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QDateEdit>
|
||||
#include <QComboBox>
|
||||
#include <QMap>
|
||||
#include "AttendanceRecord.h"
|
||||
|
||||
/**
|
||||
* @brief 统计报表对话框
|
||||
*/
|
||||
class StatisticsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit StatisticsDialog(const QVector<AttendanceRecord> &records, QWidget *parent = nullptr);
|
||||
~StatisticsDialog();
|
||||
|
||||
private slots:
|
||||
void onDateRangeChanged();
|
||||
void onExportReport();
|
||||
void onRefresh();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
void calculateStatistics();
|
||||
void updateTable();
|
||||
|
||||
// 统计数据结构
|
||||
struct StudentStats {
|
||||
QString studentId;
|
||||
QString name;
|
||||
int totalCount; // 总签到次数
|
||||
int todayCount; // 今日签到次数
|
||||
QDateTime lastCheckIn; // 最后签到时间
|
||||
double avgConfidence; // 平均置信度
|
||||
};
|
||||
|
||||
// UI组件
|
||||
QDateEdit *m_startDateEdit;
|
||||
QDateEdit *m_endDateEdit;
|
||||
QComboBox *m_quickSelectCombo;
|
||||
QTableWidget *m_tableWidget;
|
||||
QPushButton *m_exportButton;
|
||||
QPushButton *m_refreshButton;
|
||||
QLabel *m_summaryLabel;
|
||||
|
||||
// 数据
|
||||
QVector<AttendanceRecord> m_records;
|
||||
QMap<QString, StudentStats> m_statsMap;
|
||||
};
|
||||
|
||||
#endif // STATISTICSDIALOG_H
|
||||
@@ -0,0 +1,92 @@
|
||||
#ifndef TIMESLOTCONFIG_H
|
||||
#define TIMESLOTCONFIG_H
|
||||
|
||||
#include <QString>
|
||||
#include <QTime>
|
||||
#include <QVector>
|
||||
#include <QSettings>
|
||||
|
||||
/**
|
||||
* @brief 签到时间段结构
|
||||
*/
|
||||
struct TimeSlot
|
||||
{
|
||||
QString name; // 时段名称(如:上午签到)
|
||||
QTime startTime; // 开始时间
|
||||
QTime endTime; // 结束时间
|
||||
bool enabled; // 是否启用
|
||||
|
||||
TimeSlot()
|
||||
: enabled(true)
|
||||
{}
|
||||
|
||||
TimeSlot(const QString &n, const QTime &start, const QTime &end, bool en = true)
|
||||
: name(n), startTime(start), endTime(end), enabled(en)
|
||||
{}
|
||||
|
||||
// 检查给定时间是否在此时段内
|
||||
bool isInRange(const QTime &time) const {
|
||||
if (!enabled) return false;
|
||||
return time >= startTime && time <= endTime;
|
||||
}
|
||||
|
||||
QString toString() const {
|
||||
return QString("%1: %2 - %3 %4")
|
||||
.arg(name)
|
||||
.arg(startTime.toString("HH:mm"))
|
||||
.arg(endTime.toString("HH:mm"))
|
||||
.arg(enabled ? "(启用)" : "(禁用)");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 时间段配置管理类
|
||||
*/
|
||||
class TimeSlotConfig
|
||||
{
|
||||
public:
|
||||
static TimeSlotConfig& instance();
|
||||
|
||||
// 获取所有时间段
|
||||
QVector<TimeSlot> getTimeSlots() const { return m_timeSlots; }
|
||||
|
||||
// 设置时间段
|
||||
void setTimeSlots(const QVector<TimeSlot> &timeSlots);
|
||||
|
||||
// 添加时间段
|
||||
void addTimeSlot(const TimeSlot &timeSlot);
|
||||
|
||||
// 删除时间段
|
||||
void removeTimeSlot(int index);
|
||||
|
||||
// 更新时间段
|
||||
void updateTimeSlot(int index, const TimeSlot &timeSlot);
|
||||
|
||||
// 检查时间是否在任何启用的时段内
|
||||
bool isValidTime(const QTime &time) const;
|
||||
|
||||
// 获取当前时段名称
|
||||
QString getCurrentTimeSlotName(const QTime &time) const;
|
||||
|
||||
// 保存配置到文件
|
||||
void saveToFile();
|
||||
|
||||
// 从文件加载配置
|
||||
void loadFromFile();
|
||||
|
||||
// 重置为默认配置
|
||||
void resetToDefault();
|
||||
|
||||
private:
|
||||
TimeSlotConfig();
|
||||
~TimeSlotConfig();
|
||||
TimeSlotConfig(const TimeSlotConfig&) = delete;
|
||||
TimeSlotConfig& operator=(const TimeSlotConfig&) = delete;
|
||||
|
||||
QVector<TimeSlot> m_timeSlots;
|
||||
QString m_configFilePath;
|
||||
|
||||
void initDefaultTimeSlots();
|
||||
};
|
||||
|
||||
#endif // TIMESLOTCONFIG_H
|
||||
Reference in New Issue
Block a user