瀏覽代碼

网考客户端

wangyaojie 3 年之前
當前提交
3a99dd5555
共有 100 個文件被更改,包括 15127 次插入0 次删除
  1. 73 0
      client/.gitignore
  2. 1142 0
      client/answerWidget.cpp
  3. 101 0
      client/answerWidget.h
  4. 267 0
      client/answerWidget.ui
  5. 191 0
      client/awBackground.cpp
  6. 43 0
      client/awBackground.h
  7. 133 0
      client/awBackground.ui
  8. 129 0
      client/awExamScore.cpp
  9. 31 0
      client/awExamScore.h
  10. 107 0
      client/awExamScore.ui
  11. 65 0
      client/awHandinPaper.cpp
  12. 32 0
      client/awHandinPaper.h
  13. 130 0
      client/awHandinPaper.ui
  14. 99 0
      client/awMsgBox.cpp
  15. 36 0
      client/awMsgBox.h
  16. 68 0
      client/awMsgBox.ui
  17. 130 0
      client/awQuestionNavigate.cpp
  18. 39 0
      client/awQuestionNavigate.h
  19. 65 0
      client/awQuestionNavigate.ui
  20. 38 0
      client/awResumeExam.cpp
  21. 24 0
      client/awResumeExam.h
  22. 52 0
      client/awResumeExam.ui
  23. 227 0
      client/awWaitExam.cpp
  24. 37 0
      client/awWaitExam.h
  25. 111 0
      client/awWaitExam.ui
  26. 112 0
      client/awqn_item.cpp
  27. 31 0
      client/awqn_item.h
  28. 32 0
      client/awqn_item.ui
  29. 92 0
      client/clEditPassword.cpp
  30. 28 0
      client/clEditPassword.h
  31. 80 0
      client/clEditPassword.ui
  32. 113 0
      client/clMobileLogin.cpp
  33. 36 0
      client/clMobileLogin.h
  34. 81 0
      client/clMobileLogin.ui
  35. 50 0
      client/clNoticeDetail.cpp
  36. 30 0
      client/clNoticeDetail.h
  37. 111 0
      client/clNoticeDetail.ui
  38. 193 0
      client/clNoticeList.cpp
  39. 44 0
      client/clNoticeList.h
  40. 62 0
      client/clNoticeList.ui
  41. 55 0
      client/clNoticePopWidget.cpp
  42. 28 0
      client/clNoticePopWidget.h
  43. 91 0
      client/clNoticePopWidget.ui
  44. 227 0
      client/clObjectiveScore.cpp
  45. 30 0
      client/clObjectiveScore.h
  46. 39 0
      client/clObjectiveScore.ui
  47. 481 0
      client/clOfflineExam.cpp
  48. 43 0
      client/clOfflineExam.h
  49. 62 0
      client/clOfflineExam.ui
  50. 286 0
      client/clOnlineExam.cpp
  51. 38 0
      client/clOnlineExam.h
  52. 116 0
      client/clOnlineExam.ui
  53. 215 0
      client/clOnlineHomework.cpp
  54. 37 0
      client/clOnlineHomework.h
  55. 116 0
      client/clOnlineHomework.ui
  56. 190 0
      client/clOnlinePractice.cpp
  57. 36 0
      client/clOnlinePractice.h
  58. 78 0
      client/clOnlinePractice.ui
  59. 81 0
      client/clStudentInfo.cpp
  60. 30 0
      client/clStudentInfo.h
  61. 175 0
      client/clStudentInfo.ui
  62. 160 0
      client/client.pro
  63. 1957 0
      client/client.qss
  64. 227 0
      client/cloeUploadFile.cpp
  65. 38 0
      client/cloeUploadFile.h
  66. 182 0
      client/cloeUploadFile.ui
  67. 129 0
      client/cloeViewPaper.cpp
  68. 29 0
      client/cloeViewPaper.h
  69. 179 0
      client/cloeViewPaper.ui
  70. 536 0
      client/clopPaperDetail.cpp
  71. 43 0
      client/clopPaperDetail.h
  72. 42 0
      client/clopPaperDetail.ui
  73. 149 0
      client/clopPaperReport.cpp
  74. 29 0
      client/clopPaperReport.h
  75. 59 0
      client/clopPaperReport.ui
  76. 105 0
      client/clopReport.cpp
  77. 31 0
      client/clopReport.h
  78. 117 0
      client/clopReport.ui
  79. 167 0
      client/clopResultList.cpp
  80. 33 0
      client/clopResultList.h
  81. 117 0
      client/clopResultList.ui
  82. 1111 0
      client/courseList.cpp
  83. 115 0
      client/courseList.h
  84. 338 0
      client/courseList.ui
  85. 316 0
      client/environmentalTest.cpp
  86. 57 0
      client/environmentalTest.h
  87. 345 0
      client/environmentalTest.ui
  88. 207 0
      client/etCameraTest.cpp
  89. 36 0
      client/etCameraTest.h
  90. 84 0
      client/etCameraTest.ui
  91. 303 0
      client/etMobileTest.cpp
  92. 38 0
      client/etMobileTest.h
  93. 116 0
      client/etMobileTest.ui
  94. 234 0
      client/etNetworkTest.cpp
  95. 35 0
      client/etNetworkTest.h
  96. 29 0
      client/etNetworkTest.ui
  97. 274 0
      client/etTestResult.cpp
  98. 25 0
      client/etTestResult.h
  99. 39 0
      client/etTestResult.ui
  100. 177 0
      client/etTimeTest.cpp

+ 73 - 0
client/.gitignore

@@ -0,0 +1,73 @@
+# This file is used to ignore files which are generated
+# ----------------------------------------------------------------------------
+
+*~
+*.autosave
+*.a
+*.core
+*.moc
+*.o
+*.obj
+*.orig
+*.rej
+*.so
+*.so.*
+*_pch.h.cpp
+*_resource.rc
+*.qm
+.#*
+*.*#
+core
+!core/
+tags
+.DS_Store
+.directory
+*.debug
+Makefile*
+*.prl
+*.app
+moc_*.cpp
+ui_*.h
+qrc_*.cpp
+Thumbs.db
+*.res
+*.rc
+/.qmake.cache
+/.qmake.stash
+
+# qtcreator generated files
+*.pro.user*
+
+# xemacs temporary files
+*.flc
+
+# Vim temporary files
+.*.swp
+
+# Visual Studio generated files
+*.ib_pdb_index
+*.idb
+*.ilk
+*.pdb
+*.sln
+*.suo
+*.vcproj
+*vcproj.*.*.user
+*.ncb
+*.sdf
+*.opensdf
+*.vcxproj
+*vcxproj.*
+
+# MinGW generated files
+*.Debug
+*.Release
+
+# Python byte code
+*.pyc
+
+# Binaries
+# --------
+*.dll
+*.exe
+

+ 1142 - 0
client/answerWidget.cpp

@@ -0,0 +1,1142 @@
+#include "answerWidget.h"
+#include "ui_answerWidget.h"
+
+#include "CAppInfo.h"
+#include "awMsgBox.h"
+#include "awQuestionNavigate.h"
+#include "CCommonTools.h"
+#include "logproc.h"
+#include "inprogressFace.h"
+
+answerWidget::answerWidget(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::answerWidget)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+
+    qRegisterMetaType<CGetExamById>("CGetExamById");
+    connect(g_httpBllPtr.get(), &CHttpBll::sgnGetExamById, this, &answerWidget::onGetExamById);
+	qRegisterMetaType<CGetExamRecordPaperStruct>("CGetExamRecordPaperStruct");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetExamRecordPaperStruct, this, &answerWidget::onGetExamRecordPaperStruct);
+	qRegisterMetaType<CGetExamProperty>("CGetExamProperty");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetExamProperty, this, &answerWidget::onGetExamProperty);		
+	qRegisterMetaType<CWeiXinAnswerEnabled>("CWeiXinAnswerEnabled");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnWeiXinAnswerEnabled, this, &answerWidget::onWeiXinAnswerEnabled);
+	qRegisterMetaType<CGetQuestionContent>("CGetQuestionContent");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetQuestionContent, this, &answerWidget::onGetQuestionContent);
+	qRegisterMetaType<CFindExamQuestionList>("CFindExamQuestionList");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnFindExamQuestionList, this, &answerWidget::onFindExamQuestionList);
+	qRegisterMetaType<CStartAnswer>("CStartAnswer");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnStartAnswer, this, &answerWidget::onStartAnswer);
+	qRegisterMetaType<CExamHeartbeat>("CExamHeartbeat");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnExamHeartbeat, this, &answerWidget::onExamHeartbeat);
+    qRegisterMetaType<CSubmitQuestionAnswer>("CSubmitQuestionAnswer");
+    connect(g_httpBllPtr.get(), &CHttpBll::sgnSubmitQuestionAnswer, this, &answerWidget::onSubmitQuestionAnswer);
+	qRegisterMetaType<CEndExam>("CEndExam");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnEndExam, this, &answerWidget::onEndExam);
+    qRegisterMetaType<CGetOrgPropertiesByGroupWithoutCache>("CGetOrgPropertiesByGroupWithoutCache");
+    connect(g_httpBllPtr.get(), &CHttpBll::sgnGetOrgPropertiesByGroupWithoutCache, this, &answerWidget::onGetOrgPropertiesByGroupWithoutCache);
+    qRegisterMetaType<CStartFaceLiveVerify>("CStartFaceLiveVerify");
+    connect(g_httpBllPtr.get(), &CHttpBll::sgnStartFaceLiveVerify, this, &answerWidget::onStartFaceLiveVerify);
+	
+
+    m_nLeftSeconds = 0;
+
+	CHttpRequestPackage hrp;
+	hrp.sUri = QString("/api/ecs_exam_work/exam/getExamPropertyFromCacheByStudentSession/%1/%2")
+		.arg(g_appInfoPtr->m_oExamInfo.nExamId).arg("SNAPSHOT_INTERVAL,PRACTICE_TYPE,FREEZE_TIME,PRACTICE_TYPE");
+	hrp.nRequestType = RequestType::rtGetExamProperty;
+	hrp.sCommonStr = "SNAPSHOT_INTERVAL,PRACTICE_TYPE,FREEZE_TIME";
+	g_httpBllPtr->get(hrp);
+
+    {
+        CHttpRequestPackage hrp;
+        hrp.sUri = QString("/api/ecs_exam_work/exam/%1").arg(g_appInfoPtr->m_oExamInfo.nExamId);
+        hrp.nRequestType = RequestType::rtGetExamById;
+
+        g_httpBllPtr->get(hrp);
+    }
+
+	CHttpRequestPackage hrp1;
+	hrp1.sUri = "/api/ecs_oe_student/client/exam/process/getExamRecordPaperStruct";
+	hrp1.sParamList.push_back(QString("examRecordDataId,%1").arg(g_appInfoPtr->m_oExamInfo.nExamRecordDataId));
+	hrp1.nRequestType = RequestType::rtGetExamRecordPaperStruct;
+	hrp1.eParamType = HttpParamType::hptUrl;
+	g_httpBllPtr->post(hrp1);
+
+	{
+		CHttpRequestPackage hrp;
+		hrp.sUri = "/api/ecs_oe_student/client/exam/process/findExamQuestionList";		
+		hrp.nRequestType = RequestType::rtFindExamQuestionList;
+		
+		g_httpBllPtr->post(hrp);
+	}
+
+
+	{
+		CHttpRequestPackage hrp;
+		hrp.sUri = QString("/api/ecs_exam_work/exam/weixinAnswerEnabled/%1")
+			.arg(g_appInfoPtr->m_oExamInfo.nExamId);
+		hrp.nRequestType = RequestType::rtWeiXinAnswerEnabled;
+		g_httpBllPtr->get(hrp);
+	}	
+
+    m_pCountDownTimer = std::make_shared<QTimer>();
+    m_pCountDownTimer->setInterval(1000);
+    connect(m_pCountDownTimer.get(), &QTimer::timeout, this, [&](){
+		--m_nLeftSeconds;
+		ui->label_lastTime->setText(QString("%1:%2:%3").arg(m_nLeftSeconds/60/60, 2, 10, QChar('0'))
+                                    .arg(m_nLeftSeconds/60%60, 2, 10, QChar('0'))
+                                    .arg(m_nLeftSeconds%60, 2, 10, QChar('0')));
+        if(m_nLeftSeconds <= 0)
+        {
+            m_pCountDownTimer->stop();
+            handinPaper();
+        }
+
+        if(g_appInfoPtr->m_oExamInfo.bLivenessCheck &&
+                g_appInfoPtr->m_oExamInfo.nFaceLiveVerifyId > 0)
+        {
+            if(bShowLivenessTip && ((g_appInfoPtr->m_oExamInfo.nDuration - g_appInfoPtr->m_oExamInfo.nFaceVerifyDoMinute)*60 + g_appInfoPtr->m_oExamInfo.nActionAlert)  > m_nLeftSeconds)
+            {
+				bShowLivenessTip = false;
+                ShowMsg(QString::fromLocal8Bit("%1秒后开始指定动作检测,请准备!").arg(g_appInfoPtr->m_oExamInfo.nActionAlert), g_appInfoPtr->m_pAnsBgWidget);
+            }
+
+            if((g_appInfoPtr->m_oExamInfo.nDuration - g_appInfoPtr->m_oExamInfo.nFaceVerifyDoMinute)*60  > m_nLeftSeconds)
+            {                
+                //活体检测
+                if(m_pFaceLiveness == nullptr)
+                {
+                    stopCapture();
+                    m_pFaceLiveness = std::make_shared<faceLiveness>(g_appInfoPtr->m_pAnsBgWidget);
+                    connect(m_pFaceLiveness.get(), &faceLiveness::faceLivenessFaild, this, [&](){
+                        g_appInfoPtr->m_oExamInfo.nFaceLiveVerifyId = 0;
+                        m_pFaceLiveness.reset();
+                        if(g_appInfoPtr->m_oExamInfo.nFaceVerifyTimes < 2)
+                        {
+
+                            CHttpRequestPackage hrp;
+                            hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/startFaceLiveVerify")
+                                .arg(g_appInfoPtr->m_sRootOrgId);
+                            hrp.nRequestType = RequestType::rtStartFaceLiveVerify;
+                            hrp.sParamList.push_back(QString("examRecordDataId,%1").arg(g_appInfoPtr->m_oExamInfo.nExamRecordDataId));
+                            hrp.eParamType = HttpParamType::hptUrl;
+                            g_httpBllPtr->post(hrp);
+                        }
+                        startCapture();
+                    });
+
+                    connect(m_pFaceLiveness.get(), &faceLiveness::faceLivenessSucceed, this, [&](){
+                        g_appInfoPtr->m_oExamInfo.nFaceLiveVerifyId = 0;
+                        m_pFaceLiveness.reset();
+
+                        startCapture();
+                    });
+                    m_pFaceLiveness->show();
+                }
+
+            }
+        }
+    });
+
+	m_pHeartbeatTimer = std::make_shared<QTimer>();
+	m_pHeartbeatTimer->setInterval(60*1000);
+    connect(m_pHeartbeatTimer.get(), &QTimer::timeout, this, [&](){
+		heartBreat();
+    });
+}
+
+answerWidget::~answerWidget()
+{
+    delete ui;
+}
+
+void answerWidget::setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight)
+{
+    setGeometry(nLeft, nTop, nWidth, nHeight);
+    ui->widget_aw_answerBG->setGeometry(0, 0, width()*970/(970+270), height());
+    ui->widget_navigate->setGeometry(ui->widget_aw_answerBG->x() + ui->widget_aw_answerBG->width() + g_appInfoPtr->m_fRate*20, 0,
+                                           width() - ui->widget_aw_answerBG->width() - g_appInfoPtr->m_fRate*20, height());
+	ui->widget_aw_face->setGeometry(0, ui->widget_navigate->height() - g_appInfoPtr->m_fRate*140, ui->widget_navigate->width(), g_appInfoPtr->m_fRate*140);
+    ui->widget_aw_top->setGeometry(0, 0, ui->widget_aw_answerBG->width(), g_appInfoPtr->m_fRate*56);
+    ui->widget_ng_top->setGeometry(0, 0, ui->widget_navigate->width(), g_appInfoPtr->m_fRate*56);
+    ui->label_answerProgress->adjustSize();
+    ui->label_answerProgress->setGeometry(g_appInfoPtr->m_fRate*20, (ui->widget_aw_top->height() - ui->label_answerProgress->height())/2,
+                                          ui->label_answerProgress->width(), ui->label_answerProgress->height());
+    ui->pgb_answerProgress->setGeometry(ui->label_answerProgress->x() + ui->label_answerProgress->width() + g_appInfoPtr->m_fRate*8,
+                                        (ui->widget_aw_top->height() - g_appInfoPtr->m_fRate*20)/2,
+                                        g_appInfoPtr->m_fRate*200, g_appInfoPtr->m_fRate*20);
+    ui->btn_handInPaper->setGeometry(ui->widget_aw_top->width() - g_appInfoPtr->m_fRate*(10 + 100), g_appInfoPtr->m_fRate*10,
+                                     g_appInfoPtr->m_fRate*100, g_appInfoPtr->m_fRate*36);
+    ui->label_lastTimeIcon->setGeometry(g_appInfoPtr->m_fRate*48, g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*32, g_appInfoPtr->m_fRate*32);
+    ui->label_lastTime->adjustSize();
+    ui->label_lastTime->setGeometry(ui->label_lastTimeIcon->x() + ui->label_lastTimeIcon->width() + g_appInfoPtr->m_fRate*10,
+                                    ui->label_lastTimeIcon->y() + (ui->label_lastTimeIcon->height() - ui->label_lastTime->height())/2,
+                                    ui->label_lastTime->width(), ui->label_lastTime->height());
+    ui->btn_all->setGeometry(g_appInfoPtr->m_fRate*30, ui->widget_ng_top->height() + g_appInfoPtr->m_fRate*20,
+                             g_appInfoPtr->m_fRate*90, 28);
+    ui->btn_allNum->setGeometry(ui->btn_all->x() + ui->btn_all->width() - g_appInfoPtr->m_fRate*(4 + 30), ui->btn_all->y() + (ui->btn_all->height() - g_appInfoPtr->m_fRate*20)/2,
+                                g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*20);
+    ui->btn_answered->setGeometry(ui->btn_all->x() + ui->btn_all->width() + g_appInfoPtr->m_fRate*10,
+                                  ui->btn_all->y(), ui->btn_all->width(), ui->btn_all->height());
+    ui->btn_answeredNum->setGeometry(ui->btn_answered->x() + ui->btn_answered->width() - g_appInfoPtr->m_fRate*(4 + 30), ui->btn_allNum->y(),
+                                     ui->btn_allNum->width(), ui->btn_allNum->height());
+    ui->btn_unanswered->setGeometry(ui->btn_all->x(), ui->btn_all->y() + ui->btn_all->height() + g_appInfoPtr->m_fRate*10,
+                                    ui->btn_all->width(), ui->btn_all->height());
+
+    ui->btn_unansweredNum->setGeometry(ui->btn_allNum->x(), ui->btn_unanswered->y() + (ui->btn_unanswered->height() - g_appInfoPtr->m_fRate*20)/2,
+                                       ui->btn_allNum->width(), ui->btn_allNum->height());
+    ui->btn_marked->setGeometry(ui->btn_answered->x(), ui->btn_unanswered->y(), ui->btn_all->width(), ui->btn_all->height());
+    ui->btn_markedNum->setGeometry(ui->btn_answeredNum->x(), ui->btn_unansweredNum->y(), ui->btn_allNum->width(), ui->btn_allNum->height());
+    ui->label_HLine->setGeometry(ui->btn_all->x(), ui->btn_unanswered->y() + ui->btn_unanswered->height() + g_appInfoPtr->m_fRate*20,
+                                 ui->widget_navigate->width() - ui->btn_all->x()*2, g_appInfoPtr->m_fRate*1);
+	ui->lw_aw_questionList->setGeometry(0, ui->label_HLine->y(), ui->widget_navigate->width(), 
+		ui->widget_aw_face->y() - ui->label_HLine->y() - ui->label_HLine->height());
+}
+
+void answerWidget::onGetExamById(CGetExamById getExamById)
+{
+	if (getExamById.nCode == 200)
+	{
+		g_appInfoPtr->m_oExamInfo.nExamId = getExamById.nId;
+		g_appInfoPtr->m_oExamInfo.sExamType = getExamById.sExamType;
+        g_appInfoPtr->m_oExamInfo.nDuration = getExamById.nDuration;        
+	}
+	else
+	{
+        if(getExamById.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取考试信息出错"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(getExamById.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+void answerWidget::onGetExamRecordPaperStruct(CGetExamRecordPaperStruct getExamRecordPaperStruct)
+{
+	if (getExamRecordPaperStruct.nCode == 200)
+	{
+		m_vPaperGroupStruct.swap(getExamRecordPaperStruct.vPaperGroupStruct);
+		int nSize = m_vPaperGroupStruct.size();
+
+		QString sQuestionId = "";
+		for (int i = 0; i < nSize; ++i)
+		{
+                    CPaperGroupStruct &pgs = m_vPaperGroupStruct[i];
+                    awQuestionNavigate *questionItem = new awQuestionNavigate(pgs);
+                    connect(questionItem, &awQuestionNavigate::showSubQuestion, this, [&](QString sQuestionId, int nOrder, bool bHasContent) {
+						showQuestionByOrder(nOrder);
+						/*if (bHasContent)
+                        {
+                                for (CQuestionStruct &qs : pgs.vQuestionStruct)
+                                {
+                                        if (qs.sQuestionId == sQuestionId)
+                                        {
+                                                QString sGroupTitleText = QString::fromLocal8Bit("%1、%2 (共%3题)").arg(CCommonTools::Arab2Sinogram(pgs.nNumber)).arg(pgs.sGroupName).arg(pgs.nTotalSubquestion);
+                                                QString sGroupProgressText = QString::fromLocal8Bit("完成进度(%1/%2)").arg(pgs.nAnsweredCount).arg(pgs.nTotalSubquestion);
+                                                showSubQuestion(qs, nOrder, sGroupTitleText, sGroupProgressText);
+                                        }
+                                }
+                        }
+                        else
+                        {
+                                getQuestionContent(sQuestionId, nOrder);
+                        }
+						*/
+                    });
+                    QListWidgetItem *pItem = new QListWidgetItem;
+                    ui->lw_aw_questionList->addItem(pItem);
+                    ui->lw_aw_questionList->setItemWidget(pItem, questionItem);
+
+                    int nHeight = questionItem->setUI(ui->lw_aw_questionList->width() - g_appInfoPtr->m_fRate*60);
+                    QSize size = pItem->sizeHint();
+                    size.setHeight(nHeight);
+                    pItem->setSizeHint(size);
+		}							
+	}
+	else
+	{
+        if(getExamRecordPaperStruct.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取考试结构出错"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(getExamRecordPaperStruct.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+CAnsweredQuestion answerWidget::findQuestionByOrder(CFindExamQuestionList &feql, int nOrder)
+{
+	for (int i = 0; i < feql.vAnsweredQuestion.size(); ++i)
+	{
+		if (feql.vAnsweredQuestion[i].nOrder == nOrder)
+		{
+			return feql.vAnsweredQuestion[i];
+		}
+	}
+}
+
+void answerWidget::onFindExamQuestionList(CFindExamQuestionList findExamQuestionList)
+{
+	if (findExamQuestionList.nCode == 200)
+	{		
+		int nTotal = 0;
+		int nAnswered = 0;
+		int nMarked = 0;
+		int nSize = m_vPaperGroupStruct.size();
+		for (int i = 0; i < nSize; ++i)
+		{
+			int nQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct.size();
+			for (int j = 0; j < nQuestionSize; ++j)
+			{
+				int nSubQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct.size();
+				for (int k = 0; k < nSubQuestionSize; ++k)
+				{
+					nTotal++;
+					int nOrder = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct[k].nOrder;
+					
+					CAnsweredQuestion aq= findQuestionByOrder(findExamQuestionList, nOrder);
+					m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct[k].bAnswered = aq.bIsAnswer;
+					m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct[k].bMarked = aq.bIsSign;
+					m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct[k].sStudentAnswer = aq.sStudentAnswer;
+					if (aq.bIsAnswer)
+					{
+						nAnswered++;
+					}
+
+					if (aq.bIsSign)
+					{
+						nMarked++;
+					}
+
+					if (!aq.sAudioPlayTimes.isEmpty())
+					{
+						Json::Reader reader;
+						Json::Value jAudioPlayTimes = Json::Value::null;
+						if (reader.parse(aq.sAudioPlayTimes.toStdString(), jAudioPlayTimes))
+						{
+							for (int ja = 0; ja < jAudioPlayTimes.size(); ++ja)
+							{
+								Json::Value jAudioPlayItem = jAudioPlayTimes[ja];
+								g_appInfoPtr->m_jAudioPlayedCount[jAudioPlayItem["name"].asString()] = jAudioPlayItem["times"].asInt();
+							}
+						}
+					}
+				}
+			}
+		}
+		showQuestionByOrder(1);
+		ui->btn_allNum->setText(QString::number(nTotal));
+		ui->btn_answeredNum->setText(QString::number(nAnswered));
+		ui->btn_markedNum->setText(QString::number(nMarked));
+		ui->btn_unansweredNum->setText(QString::number(nTotal - nAnswered));
+		ui->pgb_answerProgress->setMaximum(nTotal);
+		ui->pgb_answerProgress->setValue(nAnswered);
+		refreshStatus();
+        //开始答题
+        CHttpRequestPackage hrp1;
+        hrp1.sUri = "/api/ecs_oe_student/client/exam/process/startAnswer";
+        hrp1.sParamList.push_back(QString("examRecordDataId,%1").arg(g_appInfoPtr->m_oExamInfo.nExamRecordDataId));
+        hrp1.nRequestType = RequestType::rtStartAnswer;
+        hrp1.eParamType = HttpParamType::hptUrl;
+        g_httpBllPtr->post(hrp1);
+	}
+	else
+	{
+        if(findExamQuestionList.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取题目信息出错"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(findExamQuestionList.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+void answerWidget::onGetExamProperty(CGetExamProperty getExamProperty)
+{
+	if (getExamProperty.nCode == 200)
+	{
+		if (getExamProperty.sType == "SNAPSHOT_INTERVAL,PRACTICE_TYPE,FREEZE_TIME")
+		{
+			m_nSnapshotInterval = getExamProperty.nSnapshotInterval;
+			g_appInfoPtr->m_oExamInfo.sPracticeType = getExamProperty.sPracticeType;
+			m_nFreezeTime = getExamProperty.nFreezeTime;
+		}
+	}
+	else
+	{
+        if(getExamProperty.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取考试信息出错"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(getExamProperty.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+void answerWidget::onWeiXinAnswerEnabled(CWeiXinAnswerEnabled weiXinAnswerEnabled)
+{
+	if (weiXinAnswerEnabled.nCode == 200)
+	{
+		g_appInfoPtr->m_bWeiXinAnswerEnabled = weiXinAnswerEnabled.bEnabled;
+	}
+	else
+	{
+        if(weiXinAnswerEnabled.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取考试信息失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(weiXinAnswerEnabled.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+void answerWidget::showSubQuestion(CQuestionStruct &qs, int nOrder, QString sTitle, QString sProgress)
+{
+	if (qs.sBody.isEmpty())
+	{
+		if (m_pQuestion != nullptr)
+		{
+			m_pQuestion.reset();
+		}
+
+		if (m_pQuestionSet != nullptr)
+		{
+			m_pQuestionSet.reset();
+		}
+		m_pQuestion = std::make_shared<question>(qs, nOrder, ui->widget_aw_answerBG);
+                connect(m_pQuestion.get(), &question::uploadAnswer, this, [&](int nIndex, __int64 timeStamp, QString sAnswer){
+                    uploadAnswer(nIndex, sAnswer);
+                });
+
+                connect(m_pQuestion.get(), &question::questionAnswered, this, [&](bool bAnswered, int nIndex){
+                    refreshStatus();
+
+                    int nSize = m_vPaperGroupStruct.size();
+                    for (int i = 0; i < nSize; ++i)
+                    {
+                        int nQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct.size();
+                        for (int j = 0; j < nQuestionSize; ++j)
+                        {
+                            int nSubQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct.size();
+                            for (int k = 0; k < nSubQuestionSize; ++k)
+                            {
+                                CSubQuestionStruct sqs = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct[k];
+                                if(sqs.nOrder == nIndex)
+                                {
+                                    QString sGroupProgressText = QString::fromLocal8Bit("完成进度(%1/%2)").arg(m_vPaperGroupStruct[i].nAnsweredCount).arg(m_vPaperGroupStruct[i].nTotalSubquestion);
+                                    m_pQuestion->setGroupInfo("", sGroupProgressText);
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                });
+
+                connect(m_pQuestion.get(), &question::markedItem, this, [&](bool bMarked, int nIndex){
+                    refreshStatus();
+                });
+
+                connect(m_pQuestion.get(), &question::previousQuestion, this, [&](int nOrder){
+                    if(nOrder > 1)
+                    {
+                        showQuestionByOrder(nOrder - 1);
+                    }
+                });
+
+                connect(m_pQuestion.get(), &question::nextQuestion, this, [&](int nOrder){
+                    showQuestionByOrder(nOrder + 1);
+                });
+		m_pQuestion->setGroupInfo(sTitle, sProgress);
+		m_pQuestion->setUI(0, g_appInfoPtr->m_fRate * 86,
+                ui->widget_aw_answerBG->width(), ui->widget_aw_answerBG->height() - g_appInfoPtr->m_fRate * (86 + 20));
+		m_pQuestion->show();
+	}
+	else
+	{
+		if (m_pQuestion != nullptr)
+		{
+			m_pQuestion.reset();
+		}
+
+		if (m_pQuestionSet != nullptr)
+		{
+			m_pQuestionSet.reset();
+		}
+		m_pQuestionSet = std::make_shared<questionSet>(qs, nOrder, ui->widget_aw_answerBG);
+                connect(m_pQuestionSet.get(), &questionSet::uploadAnswer, this, [&](int nIndex, __int64 timeStamp, QString sAnswer){
+                    uploadAnswer(nIndex, sAnswer);
+                });
+
+                connect(m_pQuestionSet.get(), &questionSet::questionAnswered, this, [&](bool bAnswered, int nIndex){
+                    refreshStatus();
+
+                    int nSize = m_vPaperGroupStruct.size();
+                    for (int i = 0; i < nSize; ++i)
+                    {
+                        int nQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct.size();
+                        for (int j = 0; j < nQuestionSize; ++j)
+                        {
+                            int nSubQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct.size();
+                            for (int k = 0; k < nSubQuestionSize; ++k)
+                            {
+                                CSubQuestionStruct sqs = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct[k];
+                                if(sqs.nOrder == nIndex)
+                                {
+                                    QString sGroupProgressText = QString::fromLocal8Bit("完成进度(%1/%2)").arg(m_vPaperGroupStruct[i].nAnsweredCount).arg(m_vPaperGroupStruct[i].nTotalSubquestion);
+                                    m_pQuestionSet->setGroupInfo("", sGroupProgressText);
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                });
+
+                connect(m_pQuestionSet.get(), &questionSet::markedItem, this, [&](bool bMarked, int nIndex){
+                    refreshStatus();
+                });
+
+                connect(m_pQuestionSet.get(), &questionSet::previousQuestion, this, [&](int nOrder){
+                    if(nOrder > 1)
+                    {
+                        showQuestionByOrder(nOrder - 1);
+                    }
+                });
+
+                connect(m_pQuestionSet.get(), &questionSet::nextQuestion, this, [&](int nOrder){
+                    showQuestionByOrder(nOrder + 1);
+                });
+		m_pQuestionSet->setGroupInfo(sTitle, sProgress);
+		m_pQuestionSet->setUI(0, g_appInfoPtr->m_fRate * 86,
+                ui->widget_aw_answerBG->width(), ui->widget_aw_answerBG->height() - g_appInfoPtr->m_fRate * (86 + 20));
+		m_pQuestionSet->show();
+	}
+}
+
+void answerWidget::refreshStatus()
+{
+    int nCount = ui->lw_aw_questionList->count();
+    for(int i = 0; i < nCount; ++i)
+    {
+        awQuestionNavigate *questionItem = dynamic_cast<awQuestionNavigate*>(ui->lw_aw_questionList->itemWidget(ui->lw_aw_questionList->item(i)));
+        if(questionItem)
+        {
+            questionItem->refreshStatus();
+        }
+    }
+
+    int nTotal = 0;
+    int nAnswered = 0;
+    int nMarked = 0;
+    int nSize = m_vPaperGroupStruct.size();
+    for (int i = 0; i < nSize; ++i)
+    {
+        int nQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct.size();
+        int nAnswerCount = 0;
+        for (int j = 0; j < nQuestionSize; ++j)
+        {
+            int nSubQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct.size();
+            for (int k = 0; k < nSubQuestionSize; ++k)
+            {
+                CSubQuestionStruct sqs = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct[k];
+                nTotal++;
+                if (sqs.bAnswered)
+                {
+                    nAnswerCount++;
+                    nAnswered++;
+                }
+
+                if (sqs.bMarked)
+                {
+                    nMarked++;
+                }
+            }
+        }
+        m_vPaperGroupStruct[i].nAnsweredCount = nAnswerCount;
+    }
+
+    ui->btn_allNum->setText(QString::number(nTotal));
+    ui->btn_answeredNum->setText(QString::number(nAnswered));
+    ui->btn_markedNum->setText(QString::number(nMarked));
+    ui->btn_unansweredNum->setText(QString::number(nTotal - nAnswered));
+    ui->pgb_answerProgress->setMaximum(nTotal);
+    ui->pgb_answerProgress->setValue(nAnswered);
+
+}
+
+void answerWidget::showQuestionByOrder(int nOrder)
+{
+    int nSize = m_vPaperGroupStruct.size();
+    for (int i = 0; i < nSize; ++i)
+    {
+        int nQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct.size();
+        for (int j = 0; j < nQuestionSize; ++j)
+        {
+            int nSubQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct.size();
+            bool bHasContent = m_vPaperGroupStruct[i].vQuestionStruct[j].bHasContent;
+            for (int k = 0; k < nSubQuestionSize; ++k)
+            {				
+                CSubQuestionStruct &sqs = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct[k];
+				sqs.bCurrent = false;
+                if(sqs.nOrder == nOrder)
+                {
+					sqs.bCurrent = true;
+                    if (bHasContent)
+                    {
+                        QString sGroupTitleText = QString::fromLocal8Bit("%1、%2 (共%3题)").arg(CCommonTools::Arab2Sinogram(m_vPaperGroupStruct[i].nNumber)).arg(m_vPaperGroupStruct[i].sGroupName).arg(m_vPaperGroupStruct[i].nTotalSubquestion);
+                        QString sGroupProgressText = QString::fromLocal8Bit("完成进度(%1/%2)").arg(m_vPaperGroupStruct[i].nAnsweredCount).arg(m_vPaperGroupStruct[i].nTotalSubquestion);
+                        showSubQuestion(m_vPaperGroupStruct[i].vQuestionStruct[j], nOrder, sGroupTitleText, sGroupProgressText);
+                    }
+                    else
+                    {
+                        getQuestionContent(m_vPaperGroupStruct[i].vQuestionStruct[j].sQuestionId, nOrder);
+                    }
+                }
+            }
+        }
+    }
+	refreshStatus();
+}
+
+void answerWidget::uploadAnswer(int nIndex, QString sAnswer)
+{
+    int nSize = m_vPaperGroupStruct.size();
+    for (int i = 0; i < nSize; ++i)
+    {
+        int nQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct.size();
+        for (int j = 0; j < nQuestionSize; ++j)
+        {
+            int nSubQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct.size();
+            for (int k = 0; k < nSubQuestionSize; ++k)
+            {
+                CSubQuestionStruct sqs = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct[k];
+                if(!sqs.bUpload)
+                {
+                    CHttpRequestPackage hrp;
+                    hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/submitQuestionAnswer");
+                    hrp.nRequestType = RequestType::rtSubmitQuestionAnswer;
+                    hrp.eParamType = HttpParamType::hptCustomBody;
+					hrp.sParamList.push_back(QString("CustomBody,%1").arg(sAnswer));
+                    hrp.sCommonStr = QString("%1,%2").arg(sqs.nOrder).arg(sqs.nVersion);
+                    g_httpBllPtr->post(hrp);
+
+                }
+            }
+        }
+    }
+}
+
+void answerWidget::onSubmitQuestionAnswer(CSubmitQuestionAnswer submitQuestionAnswer)
+{
+    if(submitQuestionAnswer.nCode == 200)
+    {
+        int nSize = m_vPaperGroupStruct.size();
+        for (int i = 0; i < nSize; ++i)
+        {
+            int nQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct.size();
+            for (int j = 0; j < nQuestionSize; ++j)
+            {
+                int nSubQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct.size();
+                for (int k = 0; k < nSubQuestionSize; ++k)
+                {
+                    CSubQuestionStruct &sqs = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct[k];
+                    if(sqs.nOrder == submitQuestionAnswer.nOrder &&
+                            submitQuestionAnswer.nVersion >= sqs.nVersion)
+                    {
+                        sqs.bUpload = true;
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    else
+    {
+        if(submitQuestionAnswer.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("提交答案失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(submitQuestionAnswer.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+    }
+}
+
+void answerWidget::getQuestionContent(QString sQuestionId, int nOrder)
+{
+	CHttpRequestPackage hrp;
+	hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/getQuestionContent")
+		.arg(g_appInfoPtr->m_oExamInfo.nExamId);
+	hrp.nRequestType = RequestType::rtGetQuestionContent;
+	hrp.eParamType = HttpParamType::hptUrl;
+    hrp.sCommonStr = sQuestionId + "," + QString::number(nOrder);
+	hrp.sParamList.push_back(QString("questionId,%1").arg(sQuestionId));
+	g_httpBllPtr->post(hrp);
+}
+
+void answerWidget::onGetQuestionContent(CGetQuestionContent getQuestionContent)
+{
+	if (getQuestionContent.nCode == 200)
+	{
+		for (CPaperGroupStruct &pgs : m_vPaperGroupStruct)
+		{
+			for (CQuestionStruct &qs : pgs.vQuestionStruct)
+			{
+				if (qs.sQuestionId == getQuestionContent.sQuestionId)
+				{
+					qs.sBody = getQuestionContent.sBody;
+					qs.sVersion = getQuestionContent.sVersion;
+					qs.bhasAudios = getQuestionContent.bhasAudios;
+					for (int i = 0; i < getQuestionContent.vSubQuestion.size(); ++i)
+					{
+						CSubQuestion sq = getQuestionContent.vSubQuestion[i];
+						qs.vSubQuestionStruct[i].sBody = sq.sBody;
+						qs.vSubQuestionStruct[i].vRightAnswer.swap(sq.vRightAnswer);
+						qs.vSubQuestionStruct[i].vOptions.swap(sq.vOptions);
+					}
+					QString sGroupTitleText = QString::fromLocal8Bit("%1、%2 (共%3题)").arg(CCommonTools::Arab2Sinogram(pgs.nNumber)).arg(pgs.sGroupName).arg(pgs.nTotalSubquestion);
+					QString sGroupProgressText = QString::fromLocal8Bit("完成进度(%1/%2)").arg(pgs.nAnsweredCount).arg(pgs.nTotalSubquestion);
+                                        showSubQuestion(qs, getQuestionContent.sOrder.toInt(), sGroupTitleText, sGroupProgressText);
+					break;
+				}
+			}
+		}		
+	}
+	else
+	{
+        if(getQuestionContent.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取题目内容失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(getQuestionContent.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+void answerWidget::onStartAnswer(CStartAnswer startAnswer)
+{
+    if(startAnswer.nCode == 200)
+    {        
+		m_pHeartbeatTimer->start();
+		heartBreat();      
+
+		//获取活体参数
+		if (g_appInfoPtr->m_oExamInfo.bLivenessCheck)
+		{
+			CHttpRequestPackage hrp;
+			hrp.sUri = QString("/api/ecs_core/org/getOrgPropertiesByGroupWithoutCache/%1/config4Edit1")
+				.arg(g_appInfoPtr->m_sRootOrgId);
+			hrp.nRequestType = RequestType::rtGetOrgPropertiesByGroupWithoutCache;
+			g_httpBllPtr->get(hrp);
+
+			{
+				CHttpRequestPackage hrp;
+				hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/startFaceLiveVerify")
+					.arg(g_appInfoPtr->m_sRootOrgId);
+				hrp.nRequestType = RequestType::rtStartFaceLiveVerify;
+				hrp.sParamList.push_back(QString("examRecordDataId,%1").arg(g_appInfoPtr->m_oExamInfo.nExamRecordDataId));
+				hrp.eParamType = HttpParamType::hptUrl;
+				g_httpBllPtr->post(hrp);
+
+			}
+		}
+    }
+    else
+    {
+        if(startAnswer.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("开始作答失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(startAnswer.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+    }
+}
+
+void answerWidget::onGetOrgPropertiesByGroupWithoutCache(CGetOrgPropertiesByGroupWithoutCache orgProperties)
+{
+    if(orgProperties.nCode == 200)
+    {		
+        g_appInfoPtr->m_oExamInfo.nActionNum = orgProperties.nActionNum;
+        g_appInfoPtr->m_oExamInfo.sActionOptions = orgProperties.sActionOptions;
+        g_appInfoPtr->m_oExamInfo.sActionOrder = orgProperties.sActionOrder;
+        g_appInfoPtr->m_oExamInfo.nActionDuration = orgProperties.nActionDuration;
+        g_appInfoPtr->m_oExamInfo.nActionAlert = orgProperties.nActionAlert;		
+    }
+    else
+    {
+        if(orgProperties.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取考试信息失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(orgProperties.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+    }
+}
+
+void answerWidget::onStartFaceLiveVerify(CStartFaceLiveVerify startFaceLiveVerify)
+{
+    if(startFaceLiveVerify.nCode == 200)
+    {
+        g_appInfoPtr->m_oExamInfo.nFaceLiveVerifyId = startFaceLiveVerify.nFaceLiveVerifyId;
+        g_appInfoPtr->m_oExamInfo.nFaceVerifyTimes = startFaceLiveVerify.nTimes;
+        g_appInfoPtr->m_oExamInfo.nFaceVerifyDoMinute = startFaceLiveVerify.nStartMinute;
+		bShowLivenessTip = true;
+    }
+    else
+    {
+        if(startFaceLiveVerify.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("开启活体验证失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(startFaceLiveVerify.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+    }
+}
+
+void answerWidget::heartBreat()
+{
+	CHttpRequestPackage hrp;
+	hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/examHeartbeat");
+	hrp.nRequestType = RequestType::rtExamHeartbeat;
+	g_httpBllPtr->post(hrp);
+}
+
+void answerWidget::onExamHeartbeat(CExamHeartbeat examHeartbeat)
+{
+	if (examHeartbeat.nCode == 200)
+	{
+		m_nLeftSeconds = examHeartbeat.nLeftTime/1000;
+        m_pCountDownTimer->start();
+        
+
+        //过程中人脸识别
+        if(g_appInfoPtr->m_oExamInfo.bFaceCheck)
+        {
+            if(g_inProcessFace == nullptr)
+            {
+                g_inProcessFace = std::make_shared<CInprogressFace>();
+                connect(g_inProcessFace.get(), &CInprogressFace::compareFailed, this, [&](QString sErrorMsg){
+                    ShowMsg(sErrorMsg, g_appInfoPtr->m_pAnsBgWidget);
+                });
+				startCapture();
+            }            
+        }
+	}
+	else
+	{
+        if(examHeartbeat.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("考试心跳提交失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(examHeartbeat.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+void answerWidget::on_btn_handInPaper_clicked()
+{
+    if((g_appInfoPtr->m_oExamInfo.nDuration - m_nFreezeTime)*60  < m_nLeftSeconds)
+    {
+        ShowMsg(QString::fromLocal8Bit("冻结时间内不允许交卷"), this);
+        return;
+    }
+
+    if(m_pHandinPaper == nullptr)
+    {
+        m_pHandinPaper = std::make_shared<awHandinPaper>(ui->btn_answeredNum->text().toInt(),
+                                                         ui->btn_unansweredNum->text().toInt(),
+                                                         ui->btn_markedNum->text().toInt(), (QWidget*)(this->parent()));
+        connect(m_pHandinPaper.get(), &awHandinPaper::handinPaper, this, [&](){
+            m_pHandinPaper.reset();
+            //交卷
+            m_pCountDownTimer->stop();
+            m_pHeartbeatTimer->stop();
+			if(g_appInfoPtr->m_oExamInfo.bLivenessCheck)
+			{ 
+				stopCapture();
+			}
+			
+			if(g_inProcessFace != nullptr)
+			{ 
+				g_inProcessFace.reset();
+			}
+			
+            handinPaper();
+        });
+
+        connect(m_pHandinPaper.get(), &awHandinPaper::cancelHandinPaper, this, [&](){
+            m_pHandinPaper.reset();
+        });
+    }
+    m_pHandinPaper->show();
+}
+
+bool answerWidget::checkAnswer()
+{
+	bool bRet = true;
+	int nSize = m_vPaperGroupStruct.size();
+	for (int i = 0; i < nSize; ++i)
+	{
+		int nQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct.size();
+		for (int j = 0; j < nQuestionSize; ++j)
+		{
+			int nSubQuestionSize = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct.size();
+			for (int k = 0; k < nSubQuestionSize; ++k)
+			{
+				CSubQuestionStruct sqs = m_vPaperGroupStruct[i].vQuestionStruct[j].vSubQuestionStruct[k];
+				if (!sqs.bUpload)
+				{		
+					bRet = false;
+					CHttpRequestPackage hrp;
+					hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/submitQuestionAnswer");
+					hrp.nRequestType = RequestType::rtSubmitQuestionAnswer;
+					hrp.eParamType = HttpParamType::hptCustomBody;
+					hrp.sParamList.push_back(QString("CustomBody,%1").arg(sqs.sUploadAnswer));
+					hrp.sCommonStr = QString("%1,%2").arg(sqs.nOrder).arg(sqs.nVersion);
+					g_httpBllPtr->post(hrp);
+				}
+			}
+		}
+	}
+	return bRet;
+}
+
+void answerWidget::handinPaper()
+{
+    if(m_pResumeExam == nullptr)
+    {
+        m_pResumeExam = std::make_shared<awResumeExam>((QWidget*)(this->parent()), QString::fromLocal8Bit("交卷中请稍候…"));
+    }
+    m_pResumeExam->show();
+
+	if (checkAnswer())
+	{
+		CHttpRequestPackage hrp;
+		hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/endExam");
+		hrp.nRequestType = RequestType::rtEndExam;
+		g_httpBllPtr->post(hrp);
+	}
+	else
+	{
+		if (m_pHandinPaperTimer == nullptr)
+		{
+			m_pHandinPaperTimer = std::make_shared<QTimer>();
+			m_pHandinPaperTimer->setInterval(10 * 1000);
+			connect(m_pHandinPaperTimer.get(), &QTimer::timeout, this, [&]() {
+				if (checkAnswer())
+				{
+                    m_pHandinPaperTimer->stop();
+					CHttpRequestPackage hrp;
+					hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/endExam");
+					hrp.nRequestType = RequestType::rtEndExam;
+					g_httpBllPtr->post(hrp);
+				}
+			});
+		}
+	}
+}
+
+void answerWidget::onEndExam(CEndExam endExam)
+{
+	if (endExam.nCode == 200)
+	{       
+        m_pResumeExam.reset();
+        if (g_appInfoPtr->m_oExamInfo.sExamType == EXAM_TYPES::PRACTICE)
+        {
+            if(g_appInfoPtr->m_oExamInfo.sPracticeType != "NO_ANSWER")
+            {
+                emit showPracticeInfo();
+            }
+            else
+            {
+                emit gobackCourseList();
+            }
+		}
+		else
+		{			
+			emit showExamScore();
+		}      	
+	}
+	else
+	{
+        if(endExam.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("交卷失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(endExam.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+void answerWidget::on_btn_all_clicked()
+{
+    int nCount = ui->lw_aw_questionList->count();
+    for(int i = 0; i < nCount; ++i)
+    {
+        awQuestionNavigate *questionItem = dynamic_cast<awQuestionNavigate*>(ui->lw_aw_questionList->itemWidget(ui->lw_aw_questionList->item(i)));
+        if(questionItem)
+        {
+            questionItem->setShowType(ITEM_SHOW_TYPE::ist_showAll);
+        }
+    }
+}
+
+void answerWidget::on_btn_answered_clicked()
+{
+    int nCount = ui->lw_aw_questionList->count();
+    for(int i = 0; i < nCount; ++i)
+    {
+        awQuestionNavigate *questionItem = dynamic_cast<awQuestionNavigate*>(ui->lw_aw_questionList->itemWidget(ui->lw_aw_questionList->item(i)));
+        if(questionItem)
+        {
+            questionItem->setShowType(ITEM_SHOW_TYPE::ist_showAnswered);
+        }
+    }
+}
+
+void answerWidget::on_btn_unanswered_clicked()
+{
+    int nCount = ui->lw_aw_questionList->count();
+    for(int i = 0; i < nCount; ++i)
+    {
+        awQuestionNavigate *questionItem = dynamic_cast<awQuestionNavigate*>(ui->lw_aw_questionList->itemWidget(ui->lw_aw_questionList->item(i)));
+        if(questionItem)
+        {
+            questionItem->setShowType(ITEM_SHOW_TYPE::ist_showUnanswered);
+        }
+    }
+}
+
+void answerWidget::on_btn_marked_clicked()
+{
+    int nCount = ui->lw_aw_questionList->count();
+    for(int i = 0; i < nCount; ++i)
+    {
+        awQuestionNavigate *questionItem = dynamic_cast<awQuestionNavigate*>(ui->lw_aw_questionList->itemWidget(ui->lw_aw_questionList->item(i)));
+        if(questionItem)
+        {
+            questionItem->setShowType(ITEM_SHOW_TYPE::ist_showMarked);
+        }
+    }
+}
+
+void answerWidget::on_btn_allNum_clicked()
+{
+    on_btn_all_clicked();
+}
+
+void answerWidget::on_btn_answeredNum_clicked()
+{
+    on_btn_answered_clicked();
+}
+
+void answerWidget::on_btn_unansweredNum_clicked()
+{
+    on_btn_unanswered_clicked();
+}
+
+void answerWidget::on_btn_markedNum_clicked()
+{
+    on_btn_marked_clicked();
+}
+
+void answerWidget::startCapture()
+{
+    if(m_pinitTimer == nullptr)
+    {
+        m_pinitTimer = std::make_shared<QTimer>();
+        m_pinitTimer->setInterval(1000);
+        connect(m_pinitTimer.get(), &QTimer::timeout, this, [&](){
+            m_pinitTimer->stop();
+            if(!m_cam.open(0))
+            {
+                ShowMsg(QString::fromLocal8Bit("打开摄像头失败"), this);
+                return;
+            }
+
+            int inWidth = ui->widget_aw_face->width();
+            m_cam.set(CV_CAP_PROP_FRAME_WIDTH, inWidth*3);
+            int inHeight = ui->widget_aw_face->height();
+            m_cam.set(CV_CAP_PROP_FRAME_HEIGHT, inHeight*3);
+            m_cam.set(CV_CAP_PROP_FPS, 5);
+            m_pVideoTimer->start();
+
+
+        });
+    }
+
+    g_inProcessFace->startFaceVerify(true);
+
+    if(m_pVideoTimer == nullptr)
+    {
+        m_pVideoTimer = std::make_shared<QTimer>();
+        m_pVideoTimer->setInterval(200);
+		m_nLastCaptureSecond = m_nLeftSeconds; 
+
+        connect(m_pVideoTimer.get(), &QTimer::timeout, this, [&](){
+
+            cv::Mat frame;
+            m_cam>>frame;
+
+            if(frame.empty())
+            {
+                qDebug()<<"widgetCameraTest frame is empty";
+                return;
+            }
+
+			if (g_inProcessFace != nullptr &&
+				m_nLastCaptureSecond - m_nLeftSeconds > g_appInfoPtr->m_oExamInfo.nSnapshotInterval)
+			{
+				m_nLastCaptureSecond = m_nLeftSeconds;
+				g_inProcessFace->addImage(frame);
+			}
+
+            QImage img = CCommonTools::Mat2QImage(frame);
+
+            ui->widget_aw_face->setAutoFillBackground(true);
+            QPalette palette;
+            palette.setBrush(QPalette::Window, QBrush(img.scaled(ui->widget_aw_face->width(), ui->widget_aw_face->height())));
+            ui->widget_aw_face->setPalette(palette);
+        });
+    }
+
+    m_pinitTimer->start();
+}
+
+void answerWidget::stopCapture()
+{
+	g_inProcessFace->startFaceVerify(false);
+    m_pVideoTimer->stop();
+    if(m_cam.isOpened())
+    {
+       m_cam.release();
+    }
+}

+ 101 - 0
client/answerWidget.h

@@ -0,0 +1,101 @@
+#ifndef ANSWERWIDGET_H
+#define ANSWERWIDGET_H
+
+#include <QWidget>
+#include <QTimer>
+#include "CHttpBll.h"
+#include "question.h"
+#include "questionSet.h"
+#include "awHandinPaper.h"
+#include "awResumeExam.h"
+#include "faceLiveness.h"
+#include <opencv2/opencv.hpp>
+
+namespace Ui {
+class answerWidget;
+}
+
+class answerWidget : public QWidget
+{
+    Q_OBJECT
+
+signals:
+    void showExamScore();
+    void showPracticeInfo();
+    void gobackCourseList();
+public:
+    explicit answerWidget(QWidget *parent = nullptr);
+    ~answerWidget();
+
+    void setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight);
+private slots:
+	void onGetExamProperty(CGetExamProperty getExamProperty);		
+	void onWeiXinAnswerEnabled(CWeiXinAnswerEnabled weiXinAnswerEnabled);
+	void onGetExamById(CGetExamById getExamById);
+	void onFindExamQuestionList(CFindExamQuestionList findExamQuestionList);
+	void onGetExamRecordPaperStruct(CGetExamRecordPaperStruct getExamRecordPaperStruct);
+	void onGetQuestionContent(CGetQuestionContent getQuestionContent);
+	void onStartAnswer(CStartAnswer startAnswer);
+	void onExamHeartbeat(CExamHeartbeat examHeartbeat);
+    void onSubmitQuestionAnswer(CSubmitQuestionAnswer submitQuestionAnswer);
+	void onEndExam(CEndExam endExam);
+    void onGetOrgPropertiesByGroupWithoutCache(CGetOrgPropertiesByGroupWithoutCache orgProperties);
+    void onStartFaceLiveVerify(CStartFaceLiveVerify startFaceLiveVerify);
+
+    void on_btn_handInPaper_clicked();
+
+    void on_btn_all_clicked();
+
+    void on_btn_answered_clicked();
+
+    void on_btn_unanswered_clicked();
+
+    void on_btn_marked_clicked();
+
+    void on_btn_allNum_clicked();
+
+    void on_btn_answeredNum_clicked();
+
+    void on_btn_unansweredNum_clicked();
+
+    void on_btn_markedNum_clicked();
+
+private:
+    void getQuestionContent(QString sQuestionId, int nOrder);
+    void showSubQuestion(CQuestionStruct &qs, int nOrder, QString sTitle, QString sProgress);
+	CAnsweredQuestion findQuestionByOrder(CFindExamQuestionList &feql, int nOrder);
+	void heartBreat();
+    void uploadAnswer(int nIndex, QString sAnswer);
+    void showQuestionByOrder(int nOrder);
+    void handinPaper();
+    void refreshStatus();
+	bool checkAnswer();
+    void startCapture();
+    void stopCapture();
+
+    Ui::answerWidget *ui;
+	
+	int m_nSnapshotInterval;
+	QString m_sPracticeType;
+	int m_nFreezeTime;	
+
+	std::vector<CPaperGroupStruct> m_vPaperGroupStruct;
+
+	std::shared_ptr<question> m_pQuestion;
+	std::shared_ptr<questionSet> m_pQuestionSet;
+    std::shared_ptr<QTimer> m_pCountDownTimer;
+    std::shared_ptr<QTimer> m_pHeartbeatTimer;
+    std::shared_ptr<awHandinPaper> m_pHandinPaper;
+    std::shared_ptr<awResumeExam> m_pResumeExam;
+	std::shared_ptr<QTimer> m_pHandinPaperTimer;
+    std::shared_ptr<faceLiveness> m_pFaceLiveness;
+    int m_nLeftSeconds;
+	bool bShowLivenessTip;
+
+    std::shared_ptr<QTimer> m_pVideoTimer;
+    std::shared_ptr<QTimer> m_pinitTimer;
+    cv::VideoCapture m_cam;
+	int m_nLastCaptureSecond;
+};
+
+#endif // ANSWERWIDGET_H

+ 267 - 0
client/answerWidget.ui

@@ -0,0 +1,267 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>answerWidget</class>
+ <widget class="QWidget" name="answerWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>944</width>
+    <height>683</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_aw_answerBG" native="true">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>0</y>
+     <width>591</width>
+     <height>571</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_aw_top" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>591</width>
+      <height>41</height>
+     </rect>
+    </property>
+    <widget class="QLabel" name="label_answerProgress">
+     <property name="geometry">
+      <rect>
+       <x>30</x>
+       <y>10</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>答题进度</string>
+     </property>
+    </widget>
+    <widget class="QProgressBar" name="pgb_answerProgress">
+     <property name="geometry">
+      <rect>
+       <x>100</x>
+       <y>10</y>
+       <width>211</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="value">
+      <number>100</number>
+     </property>
+     <property name="textVisible">
+      <bool>true</bool>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_handInPaper">
+     <property name="geometry">
+      <rect>
+       <x>500</x>
+       <y>10</y>
+       <width>75</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::NoFocus</enum>
+     </property>
+     <property name="text">
+      <string>交卷</string>
+     </property>
+    </widget>
+   </widget>
+  </widget>
+  <widget class="QWidget" name="widget_navigate" native="true">
+   <property name="geometry">
+    <rect>
+     <x>620</x>
+     <y>10</y>
+     <width>301</width>
+     <height>611</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_ng_top" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>301</width>
+      <height>51</height>
+     </rect>
+    </property>
+    <widget class="QLabel" name="label_lastTime">
+     <property name="geometry">
+      <rect>
+       <x>150</x>
+       <y>20</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>00:00:00</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_lastTimeIcon">
+     <property name="geometry">
+      <rect>
+       <x>60</x>
+       <y>20</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QPushButton" name="btn_all">
+    <property name="geometry">
+     <rect>
+      <x>30</x>
+      <y>60</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>全部</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_unanswered">
+    <property name="geometry">
+     <rect>
+      <x>30</x>
+      <y>100</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>未答</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_answered">
+    <property name="geometry">
+     <rect>
+      <x>150</x>
+      <y>60</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>已答</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_marked">
+    <property name="geometry">
+     <rect>
+      <x>150</x>
+      <y>100</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>标记</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_allNum">
+    <property name="geometry">
+     <rect>
+      <x>70</x>
+      <y>60</y>
+      <width>31</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>0</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_unansweredNum">
+    <property name="geometry">
+     <rect>
+      <x>70</x>
+      <y>100</y>
+      <width>31</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>0</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_answeredNum">
+    <property name="geometry">
+     <rect>
+      <x>190</x>
+      <y>60</y>
+      <width>31</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>0</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_markedNum">
+    <property name="geometry">
+     <rect>
+      <x>190</x>
+      <y>100</y>
+      <width>31</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>0</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_HLine">
+    <property name="geometry">
+     <rect>
+      <x>30</x>
+      <y>160</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="QListWidget" name="lw_aw_questionList">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>170</y>
+      <width>256</width>
+      <height>192</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QWidget" name="widget_aw_face" native="true">
+    <property name="geometry">
+     <rect>
+      <x>20</x>
+      <y>480</y>
+      <width>261</width>
+      <height>111</height>
+     </rect>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 191 - 0
client/awBackground.cpp

@@ -0,0 +1,191 @@
+#include "awBackground.h"
+#include "ui_awBackground.h"
+
+#include "CAppInfo.h"
+#include "awMsgBox.h"
+
+#include <QDesktopWidget>
+
+awBackground::awBackground(AW_WIDGET_TYPE wt, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::awBackground), m_wt(wt)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+    initUI();
+    g_appInfoPtr->m_pAnsBgWidget = this;
+
+    qRegisterMetaType<CGetCourseInfo>("CGetCourseInfo");
+    connect(g_httpBllPtr.get(), &CHttpBll::sgnGetCourseInfo, this, &awBackground::onGetCourseInfo);
+
+    
+}
+
+awBackground::~awBackground()
+{
+    g_appInfoPtr->m_pAnsBgWidget = nullptr;
+    delete ui;
+}
+
+void awBackground::initUI()
+{
+	//ui->label_awbg_exam->setText(QString::fromLocal8Bit("%1(%2)").arg().arg());
+	ui->label_awbg_student->setText(QString::fromLocal8Bit("%1 | %2 | %3 | %4")
+		.arg(g_appInfoPtr->m_sStudentName).arg(g_appInfoPtr->m_sStudentCode)
+		.arg(g_appInfoPtr->m_nVersionId).arg(g_appInfoPtr->m_sOrgName));
+    QDesktopWidget *dekwiget = QApplication::desktop();
+    setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+    ui->widget_awbg_BG->setGeometry(0, 0, width(), height());
+    ui->widget_awbg_top->setGeometry(0, 0, ui->widget_awbg_BG->width(), g_appInfoPtr->m_fRate*112);
+    ui->label_awbg_examIcon->setGeometry(g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*20,
+                                         g_appInfoPtr->m_fRate*16, g_appInfoPtr->m_fRate*16);
+    ui->label_awbg_exam->adjustSize();
+    ui->label_awbg_exam->setGeometry(ui->label_awbg_examIcon->x() + ui->label_awbg_examIcon->width() + g_appInfoPtr->m_fRate*10,
+                                     ui->label_awbg_examIcon->y() + (ui->label_awbg_examIcon->height() - ui->label_awbg_exam->height())/2,
+                                     ui->label_awbg_exam->width(), ui->label_awbg_exam->height());
+    ui->label_awbg_student->adjustSize();
+    ui->label_awbg_student->setGeometry(ui->widget_awbg_top->width() - g_appInfoPtr->m_fRate*30 - ui->label_awbg_student->width(),
+                                        ui->label_awbg_exam->y(), ui->label_awbg_student->width(), ui->label_awbg_student->height());
+    ui->label_awbg_studentIcon->setGeometry(ui->label_awbg_student->x() - g_appInfoPtr->m_fRate*(10 + 16),
+                                            ui->label_awbg_examIcon->y(), ui->label_awbg_examIcon->width(), ui->label_awbg_examIcon->height());
+    ui->label_cl_company->adjustSize();
+    ui->label_cl_company->setGeometry(g_appInfoPtr->m_fRate*30, ui->widget_awbg_BG->height() - g_appInfoPtr->m_fRate*10 - ui->label_cl_company->height(),
+                                      ui->label_cl_company->width(), ui->label_cl_company->height());
+    ui->label_cl_version->adjustSize();
+    ui->label_cl_version->setGeometry(ui->widget_awbg_BG->width() - g_appInfoPtr->m_fRate*30 - ui->label_cl_version->width(),
+                                      ui->label_cl_company->y(), ui->label_cl_version->width(), ui->label_cl_version->height());
+    ui->label_serverTime->adjustSize();
+    ui->label_serverTime->setGeometry(ui->label_cl_version->x() - g_appInfoPtr->m_fRate*20 - ui->label_serverTime->width(),
+                                      ui->label_cl_company->y(), ui->label_serverTime->width(), ui->label_serverTime->height());
+
+	if (m_wt == AW_WIDGET_TYPE::awwt_waitExam)
+	{
+        if(m_pWaitExam == nullptr)
+        {
+            m_pWaitExam = std::make_shared<awWaitExam>(this);
+            connect(m_pWaitExam.get(), &awWaitExam::enterExam, this, [&](){
+                m_pWaitExam.reset();
+
+				CHttpRequestPackage hrp;
+				hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/getCourseInfo/%1").arg(g_appInfoPtr->m_oExamInfo.nExamRecordDataId);
+				hrp.sParamList.push_back(QString("examRecordDataId,%1").arg(g_appInfoPtr->m_oExamInfo.nExamRecordDataId));
+				hrp.nRequestType = RequestType::rtGetCourseInfo;
+				hrp.eParamType = HttpParamType::hptUrl;
+				g_httpBllPtr->post(hrp);
+
+				m_wt = AW_WIDGET_TYPE::awwt_answerWidget;
+				if (m_pAnswerWidget == nullptr)
+				{
+					m_pAnswerWidget = std::make_shared<answerWidget>(this);
+                    connect(m_pAnswerWidget.get(), &answerWidget::showPracticeInfo, this, [&](){
+                        m_pAnswerWidget.reset();
+                        emit showPracticeInfo();
+                    });
+
+                    connect(m_pAnswerWidget.get(), &answerWidget::gobackCourseList, this, [&](){
+                        m_pAnswerWidget.reset();
+                        emit closeWidget();
+                    });
+
+                    connect(m_pAnswerWidget.get(), &answerWidget::showExamScore, this, [&](){
+                        m_pAnswerWidget.reset();
+
+                        m_wt = AW_WIDGET_TYPE::awwt_examScore;
+                        if(m_pExamScore == nullptr)
+                        {
+                            m_pExamScore = std::make_shared<awExamScore>(this);
+                            connect(m_pExamScore.get(), &awExamScore::goHomePage, this, [&](){
+                               emit closeWidget();
+                            });
+                        }
+                        m_pExamScore->setUI(g_appInfoPtr->m_fRate * 20, g_appInfoPtr->m_fRate * 56, width() - g_appInfoPtr->m_fRate * 20 * 2,
+                            height() - g_appInfoPtr->m_fRate*(56 + 38));
+                        m_pExamScore->show();
+                    });
+				}
+				m_pAnswerWidget->setUI(g_appInfoPtr->m_fRate * 20, g_appInfoPtr->m_fRate * 56, width() - g_appInfoPtr->m_fRate * 20 * 2,
+					height() - g_appInfoPtr->m_fRate*(56 + 38));
+				m_pAnswerWidget->show();
+            });
+        }
+		m_pWaitExam->setUI(g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*56, width() - g_appInfoPtr->m_fRate*20*2,
+		                       height() - g_appInfoPtr->m_fRate*(56 + 38));                            
+		m_pWaitExam->show();
+	}
+	else if (m_wt == AW_WIDGET_TYPE::awwt_answerWidget)
+	{
+		CHttpRequestPackage hrp;
+		hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/getCourseInfo/%1").arg(g_appInfoPtr->m_oExamInfo.nExamRecordDataId);
+		hrp.sParamList.push_back(QString("examRecordDataId,%1").arg(g_appInfoPtr->m_oExamInfo.nExamRecordDataId));
+		hrp.nRequestType = RequestType::rtGetCourseInfo;
+		hrp.eParamType = HttpParamType::hptUrl;
+		g_httpBllPtr->post(hrp);
+        if(m_pAnswerWidget == nullptr)
+        {
+            m_pAnswerWidget = std::make_shared<answerWidget>(this);
+            connect(m_pAnswerWidget.get(), &answerWidget::showPracticeInfo, this, [&](){
+                m_pAnswerWidget.reset();
+                emit showPracticeInfo();
+            });
+
+            connect(m_pAnswerWidget.get(), &answerWidget::gobackCourseList, this, [&](){
+                m_pAnswerWidget.reset();
+                emit closeWidget();
+            });
+
+            connect(m_pAnswerWidget.get(), &answerWidget::showExamScore, this, [&](){
+                m_pAnswerWidget.reset();
+
+                m_wt = AW_WIDGET_TYPE::awwt_examScore;
+                if(m_pExamScore == nullptr)
+                {
+                    m_pExamScore = std::make_shared<awExamScore>(this);
+                    connect(m_pExamScore.get(), &awExamScore::goHomePage, this, [&](){
+                       emit closeWidget();
+                    });
+                }
+                m_pExamScore->setUI(g_appInfoPtr->m_fRate * 20, g_appInfoPtr->m_fRate * 56, width() - g_appInfoPtr->m_fRate * 20 * 2,
+                    height() - g_appInfoPtr->m_fRate*(56 + 38));
+                m_pExamScore->show();
+            });
+        }
+		m_pAnswerWidget->setUI(g_appInfoPtr->m_fRate * 20, g_appInfoPtr->m_fRate * 56, width() - g_appInfoPtr->m_fRate * 20 * 2,
+			height() - g_appInfoPtr->m_fRate*(56 + 38));
+		m_pAnswerWidget->show();
+	}	
+	else if (m_wt == AW_WIDGET_TYPE::awwt_examScore)
+	{
+        if(m_pExamScore == nullptr)
+        {
+            m_pExamScore = std::make_shared<awExamScore>(this);
+        }
+		m_pExamScore->setUI(g_appInfoPtr->m_fRate * 20, g_appInfoPtr->m_fRate * 56, width() - g_appInfoPtr->m_fRate * 20 * 2,
+			height() - g_appInfoPtr->m_fRate*(56 + 38));
+		m_pExamScore->show();
+	}
+}
+
+void awBackground::onGetCourseInfo(CGetCourseInfo getCourseInfo)
+{
+    if (getCourseInfo.nCode == 200)
+	{
+        ui->label_awbg_exam->setText(QString::fromLocal8Bit("%1(%2)").arg(getCourseInfo.sCourseName).arg(getCourseInfo.sCourseCode));
+
+        g_appInfoPtr->m_oExamInfo.sCourseCode = getCourseInfo.sCourseCode;
+        g_appInfoPtr->m_oExamInfo.sCourseName = getCourseInfo.sCourseName;
+	}
+	else
+	{
+        if(getCourseInfo.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取科目信息失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(getCourseInfo.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+

+ 43 - 0
client/awBackground.h

@@ -0,0 +1,43 @@
+#ifndef AWBACKGROUND_H
+#define AWBACKGROUND_H
+
+#include <QWidget>
+#include "awWaitExam.h"
+#include "answerWidget.h"
+#include "awExamScore.h"
+
+namespace Ui {
+class awBackground;
+}
+
+enum class AW_WIDGET_TYPE
+{
+	awwt_waitExam = 1,
+	awwt_answerWidget,	
+	awwt_examScore
+};
+
+class awBackground : public QWidget
+{
+    Q_OBJECT
+signals:
+    void showPracticeInfo();
+    void closeWidget();
+public:
+    explicit awBackground(AW_WIDGET_TYPE wt, QWidget *parent = nullptr);
+    ~awBackground();
+private slots:
+    void onGetCourseInfo(CGetCourseInfo getCourseInfo);
+private:
+    void initUI();
+
+    Ui::awBackground *ui;
+
+    std::shared_ptr<awWaitExam> m_pWaitExam;
+    std::shared_ptr<answerWidget> m_pAnswerWidget;    
+    std::shared_ptr<awExamScore> m_pExamScore;
+
+	AW_WIDGET_TYPE m_wt;
+};
+
+#endif // AWBACKGROUND_H

+ 133 - 0
client/awBackground.ui

@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>awBackground</class>
+ <widget class="QWidget" name="awBackground">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>924</width>
+    <height>622</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_awbg_BG" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>911</width>
+     <height>661</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_awbg_top" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>941</width>
+      <height>80</height>
+     </rect>
+    </property>
+    <widget class="QLabel" name="label_awbg_examIcon">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>20</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_awbg_exam">
+     <property name="geometry">
+      <rect>
+       <x>100</x>
+       <y>20</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="frameShape">
+      <enum>QFrame::NoFrame</enum>
+     </property>
+     <property name="text">
+      <string>考试批次字段  -  语文阅读与写作(0006) -  2020-06-10 00:00:00</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_awbg_studentIcon">
+     <property name="geometry">
+      <rect>
+       <x>430</x>
+       <y>20</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_awbg_student">
+     <property name="geometry">
+      <rect>
+       <x>500</x>
+       <y>20</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>文闹闹  |  T20190123  |  402000201010202827  |  测试中心</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QLabel" name="label_serverTime">
+    <property name="geometry">
+     <rect>
+      <x>310</x>
+      <y>590</y>
+      <width>191</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>服务器时间:2021-08-10 14:42:27</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_cl_version">
+    <property name="geometry">
+     <rect>
+      <x>510</x>
+      <y>590</y>
+      <width>81</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>版本号:V1.0</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_cl_company">
+    <property name="geometry">
+     <rect>
+      <x>20</x>
+      <y>590</y>
+      <width>241</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>技术支持:武汉启明泰和软件服务有限公司</string>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 129 - 0
client/awExamScore.cpp

@@ -0,0 +1,129 @@
+#include "awExamScore.h"
+#include "ui_awExamScore.h"
+
+#include "CAppInfo.h"
+#include "awMsgBox.h"
+
+awExamScore::awExamScore(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::awExamScore)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	qRegisterMetaType<CGetExamProperty>("CGetExamProperty");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetExamProperty, this, &awExamScore::onGetExamProperty);
+
+	CHttpRequestPackage hrp;
+	hrp.sUri = QString("/api/ecs_exam_work/exam/getExamPropertyFromCacheByStudentSession/%1/%2")
+		.arg(g_appInfoPtr->m_oExamInfo.nExamId).arg("SNAPSHOT_INTERVAL,PRACTICE_TYPE,FREEZE_TIME");
+	hrp.nRequestType = RequestType::rtGetExamProperty;
+	hrp.sCommonStr = "AFTER_EXAM_REMARK,IS_OBJ_SCORE_VIEW,SHOW_CHEATING_REMARK,CHEATING_REMARK";
+	g_httpBllPtr->get(hrp);
+}
+
+awExamScore::~awExamScore()
+{
+    delete ui;
+}
+
+void awExamScore::setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight)
+{
+    setGeometry(nLeft, nTop, nWidth, nHeight);
+    ui->widget_awes_BG->setGeometry(0, 0, nWidth, nHeight);
+    ui->label_awes_title->adjustSize();
+    ui->label_awes_title->setGeometry((ui->widget_awes_BG->width() - ui->label_awes_title->width())/2, g_appInfoPtr->m_fRate*58,
+                                      ui->label_awes_title->width(), ui->label_awes_title->height());
+    ui->label_awes_ptoto->setGeometry((ui->widget_awes_BG->width() - g_appInfoPtr->m_fRate*90)/2,
+                                      ui->label_awes_title->y() + ui->label_awes_title->height() + g_appInfoPtr->m_fRate*20,
+                                      g_appInfoPtr->m_fRate*90, g_appInfoPtr->m_fRate*120);
+    QString sFileName = g_appInfoPtr->m_sStudentPhotoPath.right(g_appInfoPtr->m_sStudentPhotoPath.length() - g_appInfoPtr->m_sStudentPhotoPath.lastIndexOf("/") - 1);
+    sFileName = g_appInfoPtr->m_sCacheFileDir + sFileName;
+
+    ui->label_awes_ptoto->setPixmap(QPixmap(sFileName).scaled(ui->label_awes_ptoto->width(), ui->label_awes_ptoto->height(),
+                                                                Qt::IgnoreAspectRatio));
+    ui->label_awes_result->adjustSize();
+    ui->label_awes_result->setGeometry((ui->widget_awes_BG->width() - ui->label_awes_result->width())/2, ui->label_awes_ptoto->y() + ui->label_awes_ptoto->height() + g_appInfoPtr->m_fRate*20,
+                                       ui->label_awes_result->width(), ui->label_awes_result->height());
+    ui->txtb_awes_postNotice->setGeometry(g_appInfoPtr->m_fRate*40, ui->label_awes_result->y() + ui->label_awes_result->height() + g_appInfoPtr->m_fRate*40,
+                                          ui->widget_awes_BG->width() - g_appInfoPtr->m_fRate*40*2, g_appInfoPtr->m_fRate*164);
+    ui->txtb_awes_breachNotice->setVisible(false);
+    ui->txtb_awes_breachNotice->setGeometry(ui->txtb_awes_postNotice->x() + ui->txtb_awes_postNotice->width() + g_appInfoPtr->m_fRate*20,
+                                            ui->txtb_awes_postNotice->y(), ui->txtb_awes_postNotice->width(), ui->txtb_awes_postNotice->height());
+    ui->btn_awes_goback->setGeometry((ui->widget_awes_BG->width() - g_appInfoPtr->m_fRate*200)/2, ui->txtb_awes_postNotice->y() + ui->txtb_awes_postNotice->height() + g_appInfoPtr->m_fRate*30,
+                                     g_appInfoPtr->m_fRate*200, g_appInfoPtr->m_fRate*40);
+}
+
+void awExamScore::onGetExamProperty(CGetExamProperty getExamProperty)
+{
+	if (getExamProperty.nCode == 200)
+	{
+		if (getExamProperty.sType == "AFTER_EXAM_REMARK,IS_OBJ_SCORE_VIEW,SHOW_CHEATING_REMARK,CHEATING_REMARK")
+		{			
+			if (getExamProperty.bShowCheatingRemark)
+			{
+                ui->txtb_awes_breachNotice->setVisible(true);
+				ui->txtb_awes_postNotice->setGeometry(g_appInfoPtr->m_fRate * 40, ui->label_awes_result->y() + ui->label_awes_result->height() + g_appInfoPtr->m_fRate * 40,
+					(ui->widget_awes_BG->width() - g_appInfoPtr->m_fRate * 40 * 2 - g_appInfoPtr->m_fRate*20)/2, g_appInfoPtr->m_fRate * 164);
+				ui->txtb_awes_breachNotice->setGeometry(ui->txtb_awes_postNotice->x() + ui->txtb_awes_postNotice->width() + g_appInfoPtr->m_fRate * 20,
+					ui->txtb_awes_postNotice->y(), ui->txtb_awes_postNotice->width(), ui->txtb_awes_postNotice->height());
+				ui->txtb_awes_breachNotice->insertHtml(getExamProperty.sCheatingRemark);
+			}
+
+			ui->txtb_awes_postNotice->insertHtml(getExamProperty.sAfterExamRemark);
+
+			if (getExamProperty.bIsObjScoreView)
+			{
+				//获取客观分
+				ui->label_awes_result->setText(QString::fromLocal8Bit("考试结果计算中..."));
+				CHttpRequestPackage hrp;
+				hrp.sUri = QString("/api/ecs_oe_student/client/exam/process/getEndExamInfo");
+				hrp.sParamList.push_back(QString("examRecordDataId,%1").arg(g_appInfoPtr->m_oExamInfo.nExamRecordDataId));
+				hrp.nRequestType = RequestType::rtGetEndExamInfo;				
+				hrp.eParamType = HttpParamType::hptUrl;
+				g_httpBllPtr->post(hrp);
+			}
+			else
+			{
+				//显示交卷成功
+				ui->label_awes_result->setText(QString::fromLocal8Bit("交卷完成"));
+			}
+		}
+	}
+	else
+	{
+        if(getExamProperty.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取考试信息失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(getExamProperty.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+void awExamScore::onGetEndExamInfo(CGetEndExamInfo getEndExamInfo)
+{
+	if (getEndExamInfo.nCode == 200)
+	{
+		ui->label_awes_result->setText(QString::fromLocal8Bit("客观题得分:%1").arg(getEndExamInfo.fObjectiveScore));
+	}
+	else
+	{
+        if(getEndExamInfo.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取考试信息失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(getEndExamInfo.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+void awExamScore::on_btn_awes_goback_clicked()
+{
+    emit goHomePage();
+}

+ 31 - 0
client/awExamScore.h

@@ -0,0 +1,31 @@
+#ifndef AWEXAMSCORE_H
+#define AWEXAMSCORE_H
+
+#include <QWidget>
+
+#include "CHttpBll.h"
+
+namespace Ui {
+class awExamScore;
+}
+
+class awExamScore : public QWidget
+{
+    Q_OBJECT
+signals:
+    void goHomePage();
+public:
+    explicit awExamScore(QWidget *parent = nullptr);
+    ~awExamScore();
+
+    void setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight);
+private slots:
+	void onGetExamProperty(CGetExamProperty getExamProperty);
+	void onGetEndExamInfo(CGetEndExamInfo getEndExamInfo);
+    void on_btn_awes_goback_clicked();
+
+private:
+    Ui::awExamScore *ui;
+};
+
+#endif // AWEXAMSCORE_H

+ 107 - 0
client/awExamScore.ui

@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>awExamScore</class>
+ <widget class="QWidget" name="awExamScore">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>941</width>
+    <height>563</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_awes_BG" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>10</y>
+     <width>721</width>
+     <height>591</height>
+    </rect>
+   </property>
+   <widget class="QLabel" name="label_awes_title">
+    <property name="geometry">
+     <rect>
+      <x>290</x>
+      <y>60</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>考试已结束</string>
+    </property>
+    <property name="alignment">
+     <set>Qt::AlignCenter</set>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_awes_ptoto">
+    <property name="geometry">
+     <rect>
+      <x>280</x>
+      <y>100</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_awes_result">
+    <property name="geometry">
+     <rect>
+      <x>280</x>
+      <y>170</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>考试结果计算中…</string>
+    </property>
+    <property name="alignment">
+     <set>Qt::AlignCenter</set>
+    </property>
+   </widget>
+   <widget class="QTextBrowser" name="txtb_awes_postNotice">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>210</y>
+      <width>256</width>
+      <height>192</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QTextBrowser" name="txtb_awes_breachNotice">
+    <property name="geometry">
+     <rect>
+      <x>360</x>
+      <y>210</y>
+      <width>256</width>
+      <height>192</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_awes_goback">
+    <property name="geometry">
+     <rect>
+      <x>310</x>
+      <y>430</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>返回主页</string>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 65 - 0
client/awHandinPaper.cpp

@@ -0,0 +1,65 @@
+#include "awHandinPaper.h"
+#include "ui_awHandinPaper.h"
+
+#include "CAppInfo.h"
+
+#include <QDesktopWidget>
+
+awHandinPaper::awHandinPaper(int nAnswered, int nUnanswered, int nMarked, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::awHandinPaper)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+    ui->label_awhp_answered->setText(QString::fromLocal8Bit("已答题目:%1").arg(nAnswered));
+    ui->label_awhp_unanswered->setText(QString::fromLocal8Bit("未答题目:%1").arg(nUnanswered));
+    ui->label_awhp_marked->setText(QString::fromLocal8Bit("标记题目:%1").arg(nMarked));
+
+    initUI();
+}
+
+awHandinPaper::~awHandinPaper()
+{
+    delete ui;
+}
+
+void awHandinPaper::initUI()
+{
+    QDesktopWidget *dekwiget = QApplication::desktop();
+    setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+    ui->widget_mask->setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+    ui->widget_awhp_BG->setGeometry((width() - g_appInfoPtr->m_fRate*420)/2, (height() - g_appInfoPtr->m_fRate*227)/2,
+                                   g_appInfoPtr->m_fRate*420, g_appInfoPtr->m_fRate*227);
+    ui->label_awhp_icon->setGeometry(g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*30,
+                                    g_appInfoPtr->m_fRate*40, g_appInfoPtr->m_fRate*40);
+    ui->label_awhp_title->adjustSize();
+    ui->label_awhp_title->setGeometry(ui->label_awhp_icon->x() + ui->label_awhp_icon->width() + g_appInfoPtr->m_fRate*20,
+                                      ui->label_awhp_icon->y(), ui->label_awhp_title->width(), ui->label_awhp_title->height());
+    ui->label_awhp_answered->adjustSize();
+    ui->label_awhp_answered->setGeometry(ui->label_awhp_title->x(), ui->label_awhp_title->y() + ui->label_awhp_title->height() + g_appInfoPtr->m_fRate*20,
+                                         ui->label_awhp_answered->width(), ui->label_awhp_answered->height());
+    ui->label_awhp_unanswered->adjustSize();
+    ui->label_awhp_unanswered->setGeometry(ui->label_awhp_answered->x(), ui->label_awhp_answered->y() + ui->label_awhp_answered->height() + g_appInfoPtr->m_fRate*5,
+                                           ui->label_awhp_unanswered->width(), ui->label_awhp_unanswered->height());
+    ui->label_awhp_marked->adjustSize();
+    ui->label_awhp_marked->setGeometry(ui->label_awhp_unanswered->x(), ui->label_awhp_unanswered->y() + ui->label_awhp_unanswered->height() + g_appInfoPtr->m_fRate*5,
+                                       ui->label_awhp_marked->width(), ui->label_awhp_marked->height());
+    ui->btn_awhp_comfirm->setGeometry(ui->widget_awhp_BG->width() - g_appInfoPtr->m_fRate*(20 + 80),
+                                      ui->widget_awhp_BG->height() - g_appInfoPtr->m_fRate*(20 + 30),
+                                      g_appInfoPtr->m_fRate*80, g_appInfoPtr->m_fRate*30);
+    ui->btn_awhp_cancel->setGeometry(ui->btn_awhp_comfirm->x() - g_appInfoPtr->m_fRate*10 - ui->btn_awhp_comfirm->width(), ui->btn_awhp_comfirm->y(),
+                                     ui->btn_awhp_comfirm->width(), ui->btn_awhp_comfirm->height());
+
+}
+
+void awHandinPaper::on_btn_awhp_cancel_clicked()
+{
+    emit cancelHandinPaper();
+}
+
+void awHandinPaper::on_btn_awhp_comfirm_clicked()
+{
+    emit handinPaper();
+}

+ 32 - 0
client/awHandinPaper.h

@@ -0,0 +1,32 @@
+#ifndef AWHANDINPAPER_H
+#define AWHANDINPAPER_H
+
+#include <QWidget>
+
+namespace Ui {
+class awHandinPaper;
+}
+
+class awHandinPaper : public QWidget
+{
+    Q_OBJECT
+signals:
+    void handinPaper();
+    void cancelHandinPaper();
+public:
+    explicit awHandinPaper(int nAnswered, int nUnanswered, int nMarked, QWidget *parent = nullptr);
+    ~awHandinPaper();
+
+
+private slots:
+    void on_btn_awhp_cancel_clicked();
+
+    void on_btn_awhp_comfirm_clicked();
+
+private:
+    void initUI();
+
+    Ui::awHandinPaper *ui;
+};
+
+#endif // AWHANDINPAPER_H

+ 130 - 0
client/awHandinPaper.ui

@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>awHandinPaper</class>
+ <widget class="QWidget" name="awHandinPaper">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>648</width>
+    <height>518</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_mask" native="true">
+   <property name="geometry">
+    <rect>
+     <x>140</x>
+     <y>30</y>
+     <width>491</width>
+     <height>391</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_awhp_BG" native="true">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>30</y>
+      <width>321</width>
+      <height>221</height>
+     </rect>
+    </property>
+    <widget class="QLabel" name="label_awhp_icon">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>30</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_awhp_title">
+     <property name="geometry">
+      <rect>
+       <x>120</x>
+       <y>30</y>
+       <width>141</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>确认交卷</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_awhp_answered">
+     <property name="geometry">
+      <rect>
+       <x>110</x>
+       <y>80</y>
+       <width>81</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>已答题目:10</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_awhp_unanswered">
+     <property name="geometry">
+      <rect>
+       <x>110</x>
+       <y>120</y>
+       <width>101</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>未答题目:21</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_awhp_comfirm">
+     <property name="geometry">
+      <rect>
+       <x>190</x>
+       <y>190</y>
+       <width>75</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>确定</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_awhp_marked">
+     <property name="geometry">
+      <rect>
+       <x>110</x>
+       <y>160</y>
+       <width>91</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>标记题目:0</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_awhp_cancel">
+     <property name="geometry">
+      <rect>
+       <x>100</x>
+       <y>190</y>
+       <width>75</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>取消</string>
+     </property>
+    </widget>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 99 - 0
client/awMsgBox.cpp

@@ -0,0 +1,99 @@
+#include "awMsgBox.h"
+#include "ui_awMsgBox.h"
+
+#include "CAppInfo.h"
+
+#include <QDesktopWidget>
+#include <QGraphicsDropShadowEffect>
+
+std::list<std::shared_ptr<awMsgBox>> awMsgBox::vMsgList;
+std::mutex awMsgBox::msgMutex;
+
+awMsgBox::awMsgBox(QWidget *parent) :
+    QDialog(parent),
+    ui(new Ui::awMsgBox)
+{
+    ui->setupUi(this);
+
+    setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
+    setAttribute(Qt::WA_TranslucentBackground);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	if(m_pTimer == nullptr)
+	{ 
+		m_pTimer = std::make_shared<QTimer>();
+		m_pTimer->setInterval(3 * 1000);		
+	}    
+	
+	connect(m_pTimer.get(), &QTimer::timeout, this, [&]() {
+		m_pTimer->stop();
+
+		if (vMsgList.size() > 0)
+		{
+			std::scoped_lock lock(msgMutex);					
+			vMsgList.pop_front();
+		}						
+		size_t i = 0;
+		for (std::list<std::shared_ptr<awMsgBox>>::iterator it = vMsgList.begin(); it != vMsgList.end(); ++it)
+		{			
+			(*it)->setGeometry((*it)->x(),
+				g_appInfoPtr->m_fRate * 84 + i * g_appInfoPtr->m_fRate * 90,
+				(*it)->width(), (*it)->height());
+			++i;
+		}		
+	});
+
+    m_pTimer->start();    
+}
+
+awMsgBox::~awMsgBox()
+{
+	m_pTimer->stop();
+	
+    delete ui;
+}
+
+void awMsgBox::initUI()
+{
+    ui->label_awmb_content->adjustSize();
+    int nCW = g_appInfoPtr->m_fRate*40 + g_appInfoPtr->m_fRate*40 + g_appInfoPtr->m_fRate*(28 + 10) + ui->label_awmb_content->width();
+    QDesktopWidget *dekwiget = QApplication::desktop();
+
+    setGeometry((dekwiget->width() - nCW - g_appInfoPtr->m_fRate*10)/2,
+                g_appInfoPtr->m_fRate*84 + vMsgList.size()*g_appInfoPtr->m_fRate * 90, 
+				nCW + g_appInfoPtr->m_fRate*10, g_appInfoPtr->m_fRate*90);
+    ui->widget_awmb_BG->setGeometry(5, 5 , nCW, g_appInfoPtr->m_fRate*80);
+    ui->label_awmb_icon->setGeometry(g_appInfoPtr->m_fRate*40, g_appInfoPtr->m_fRate*26,
+                                   g_appInfoPtr->m_fRate*28, g_appInfoPtr->m_fRate*28);
+    ui->label_awmb_content->setGeometry(ui->label_awmb_icon->x() + ui->label_awmb_icon->width() + g_appInfoPtr->m_fRate*10,
+                                   (height() - ui->label_awmb_content->height())/2,
+                                   ui->label_awmb_content->width(), ui->label_awmb_content->height());
+    ui->btn_awmb_close->setGeometry(ui->widget_awmb_BG->width() - g_appInfoPtr->m_fRate*(10 + 16),
+                                    g_appInfoPtr->m_fRate*10, g_appInfoPtr->m_fRate*16, g_appInfoPtr->m_fRate*16);
+    QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect;
+    effect->setOffset(g_appInfoPtr->m_fRate*4,g_appInfoPtr->m_fRate*4);
+    effect->setColor(QColor(0,0,0,30));
+    effect->setBlurRadius(g_appInfoPtr->m_fRate*16);
+    ui->widget_awmb_BG->setGraphicsEffect(effect);
+}
+
+void ShowMsg(QString sMsg, QWidget *parent)
+{
+	std::shared_ptr<awMsgBox> msg = std::make_shared<awMsgBox>(parent);
+    msg->setMsg(sMsg);
+    msg->initUI();
+    msg->show();
+	std::scoped_lock lock(awMsgBox::msgMutex);
+	awMsgBox::vMsgList.push_back(msg);
+}
+
+void awMsgBox::setMsg(QString sMsg)
+{
+    ui->label_awmb_content->setText(sMsg);
+}
+
+void awMsgBox::on_btn_awmb_close_clicked()
+{
+    reject();
+}

+ 36 - 0
client/awMsgBox.h

@@ -0,0 +1,36 @@
+#ifndef AWMSGBOX_H
+#define AWMSGBOX_H
+
+#include <QDialog>
+#include <mutex>
+
+namespace Ui {
+class awMsgBox;
+}
+
+#include <QTimer>
+
+class awMsgBox : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit awMsgBox(QWidget *parent = nullptr);
+    ~awMsgBox();
+
+    void initUI();
+    void setMsg(QString sMsg);
+	static std::list< std::shared_ptr<awMsgBox>> vMsgList;
+	static std::mutex msgMutex;
+private slots:
+    void on_btn_awmb_close_clicked();
+
+private:
+    Ui::awMsgBox *ui;
+    std::shared_ptr<QTimer> m_pTimer;	
+
+};
+
+extern void ShowMsg(QString sMsg, QWidget *parent = nullptr);
+
+#endif // AWMSGBOX_H

+ 68 - 0
client/awMsgBox.ui

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>awMsgBox</class>
+ <widget class="QWidget" name="awMsgBox">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>333</width>
+    <height>70</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_awmb_BG" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>331</width>
+     <height>71</height>
+    </rect>
+   </property>
+   <widget class="QLabel" name="label_awmb_icon">
+    <property name="geometry">
+     <rect>
+      <x>30</x>
+      <y>30</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_awmb_content">
+    <property name="geometry">
+     <rect>
+      <x>110</x>
+      <y>30</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>请调整坐姿,诚信考试</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_awmb_close">
+    <property name="geometry">
+     <rect>
+      <x>294</x>
+      <y>10</y>
+      <width>31</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 130 - 0
client/awQuestionNavigate.cpp

@@ -0,0 +1,130 @@
+#include "awQuestionNavigate.h"
+#include "ui_awQuestionNavigate.h"
+
+#include "CAppInfo.h"
+
+#include "CCommonTools.h"
+
+#include <QGridLayout>
+
+awQuestionNavigate::awQuestionNavigate(CPaperGroupStruct &pgs, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::awQuestionNavigate), m_pgs(pgs)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+}
+
+awQuestionNavigate::~awQuestionNavigate()
+{
+    delete ui;
+}
+
+int awQuestionNavigate::setUI(const int nWidth)
+{
+	ui->label_questionName->setText(QString::fromLocal8Bit("%1%2").arg(CCommonTools::Arab2Sinogram(m_pgs.nNumber)).arg(m_pgs.sGroupName));
+	ui->label_questionScore->setText(QString::fromLocal8Bit("%1分").arg(m_pgs.fGroupScore));
+	ui->widget_qn_groupInfo->setGeometry(0, 0, nWidth, g_appInfoPtr->m_fRate*50);
+    ui->label_questionName->adjustSize();
+    ui->label_questionName->setGeometry(0, g_appInfoPtr->m_fRate*20, ui->label_questionName->width(), ui->label_questionName->height());
+    ui->label_questionScore->adjustSize();
+    ui->label_questionScore->setGeometry(ui->widget_qn_groupInfo->width() - ui->label_questionScore->width(), ui->label_questionName->y() + (ui->label_questionName->height() - ui->label_questionScore->height())/2,
+                                         ui->label_questionScore->width(), ui->label_questionScore->height());
+	QGridLayout *layout = new QGridLayout;
+	layout->setHorizontalSpacing(g_appInfoPtr->m_fRate*10);
+	layout->setVerticalSpacing(g_appInfoPtr->m_fRate*10);
+	layout->setAlignment(Qt::AlignLeft);
+	layout->setContentsMargins(0, 0, 0, 0);
+	
+    m_vItemList.clear();
+    int nTotal = 0;
+    int nQuestionSize = m_pgs.vQuestionStruct.size();
+    for(int i = 0; i < nQuestionSize; ++i)
+    {
+        CQuestionStruct &qs = m_pgs.vQuestionStruct[i];
+        int nSubQuestionSize = qs.vSubQuestionStruct.size();
+        for(int j = 0; j < nSubQuestionSize; ++j)
+        {
+            CSubQuestionStruct &sqs = qs.vSubQuestionStruct[j];
+            awqn_item *item = new awqn_item(sqs);
+            connect(item, &awqn_item::showSubQuestion, this, [&](int nOrder) {
+                    emit showSubQuestion(qs.sQuestionId, nOrder, qs.bHasContent);
+			});
+			item->setFixedSize(g_appInfoPtr->m_fRate * 30, g_appInfoPtr->m_fRate * 30);
+			layout->addWidget(item, (sqs.nShowNumber + 4)/5 - 1, (sqs.nShowNumber - 1)%5, 1, 1);
+			item->setUI();
+            m_vItemList.push_back(item);
+            nTotal++;
+        }
+    }
+    int nRow = (nTotal + 4)/5;
+    ui->widget_qn_questionList->setGeometry(0, ui->widget_qn_groupInfo->y() + ui->widget_qn_groupInfo->height(),
+                                            nWidth, nRow*40*g_appInfoPtr->m_fRate);
+	ui->widget_qn_questionList->setLayout(layout);	
+    setGeometry(0, 0, nWidth, ui->widget_qn_questionList->height() + ui->widget_qn_groupInfo->height());
+    return height();
+}
+
+void awQuestionNavigate::refreshStatus()
+{
+    int nSize = m_vItemList.size();
+    for(int i = 0; i < nSize; ++i)
+    {
+        awqn_item *item = m_vItemList[i];
+        if(item)
+        {
+            item->refreshStatus();
+        }
+    }
+}
+
+void awQuestionNavigate::setShowType(ITEM_SHOW_TYPE type)
+{
+    int nIndex = 1;
+    int nSize = m_vItemList.size();
+    for(int i = 0; i < nSize; ++i)
+    {
+        awqn_item *item = m_vItemList[i];
+        if(item)
+        {
+            if(type == ITEM_SHOW_TYPE::ist_showAll)
+            {
+                item->setVisible(true);
+            }
+            else if(type == ITEM_SHOW_TYPE::ist_showMarked)
+            {
+                if(item->isMarked())
+                {
+                    item->setVisible(true);
+                }
+                else
+                {
+                    item->setVisible(false);
+                }
+            }
+            else if(type == ITEM_SHOW_TYPE::ist_showAnswered)
+            {
+                if(item->isAnswered())
+                {
+                    item->setVisible(true);
+                }
+                else
+                {
+                    item->setVisible(false);
+                }
+            }
+            else if(type == ITEM_SHOW_TYPE::ist_showUnanswered)
+            {
+                if(!item->isAnswered())
+                {
+                    item->setVisible(true);
+                }
+                else
+                {
+                    item->setVisible(false);
+                }
+            }
+        }
+    }
+}

+ 39 - 0
client/awQuestionNavigate.h

@@ -0,0 +1,39 @@
+#ifndef AWQUESTIONNAVIGATE_H
+#define AWQUESTIONNAVIGATE_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+#include "awqn_item.h"
+
+namespace Ui {
+class awQuestionNavigate;
+}
+
+enum class ITEM_SHOW_TYPE
+{
+    ist_showAll = 1,
+    ist_showMarked,
+    ist_showAnswered,
+    ist_showUnanswered
+};
+
+class awQuestionNavigate : public QWidget
+{
+    Q_OBJECT
+signals:
+    void showSubQuestion(QString sQuestionId, int nOrder, bool bHasContent);
+public:
+    explicit awQuestionNavigate(CPaperGroupStruct &pgs, QWidget *parent = nullptr);
+    ~awQuestionNavigate();
+
+    int setUI(const int nWidth);
+    void refreshStatus();
+    void setShowType(ITEM_SHOW_TYPE type);
+private:
+    Ui::awQuestionNavigate *ui;
+
+    CPaperGroupStruct &m_pgs;
+    std::vector<awqn_item*> m_vItemList;
+};
+
+#endif // AWQUESTIONNAVIGATE_H

+ 65 - 0
client/awQuestionNavigate.ui

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>awQuestionNavigate</class>
+ <widget class="QWidget" name="awQuestionNavigate">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>353</width>
+    <height>107</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_qn_groupInfo" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>351</width>
+     <height>51</height>
+    </rect>
+   </property>
+   <widget class="QLabel" name="label_questionScore">
+    <property name="geometry">
+     <rect>
+      <x>270</x>
+      <y>20</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>40分</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_questionName">
+    <property name="geometry">
+     <rect>
+      <x>20</x>
+      <y>20</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>一 单选题</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QWidget" name="widget_qn_questionList" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>50</y>
+     <width>381</width>
+     <height>80</height>
+    </rect>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 38 - 0
client/awResumeExam.cpp

@@ -0,0 +1,38 @@
+#include "awResumeExam.h"
+#include "ui_awResumeExam.h"
+
+
+#include "CAppInfo.h"
+
+#include <QDesktopWidget>
+
+awResumeExam::awResumeExam(QWidget *parent, QString sText) :
+    QWidget(parent),
+    ui(new Ui::awResumeExam)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	ui->label_awhp_title->setText(sText);
+    initUI();
+}
+
+awResumeExam::~awResumeExam()
+{
+    delete ui;
+}
+
+void awResumeExam::initUI()
+{
+    QDesktopWidget *dekwiget = QApplication::desktop();
+    setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+    ui->widget_mask->setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+	ui->label_awhp_title->adjustSize();
+    ui->widget_awre_BG->setGeometry((width() - ui->label_awhp_title->width() - g_appInfoPtr->m_fRate*48*2)/2, (height() - g_appInfoPtr->m_fRate*73)/2,
+		ui->label_awhp_title->width() + g_appInfoPtr->m_fRate * 48 * 2, g_appInfoPtr->m_fRate*73);
+    
+    ui->label_awhp_title->setGeometry((ui->widget_awre_BG->width() - ui->label_awhp_title->width())/2,
+                                      (ui->widget_awre_BG->height() - ui->label_awhp_title->height())/2,
+                                      ui->label_awhp_title->width(), ui->label_awhp_title->height());
+}

+ 24 - 0
client/awResumeExam.h

@@ -0,0 +1,24 @@
+#ifndef AWRESUMEEXAM_H
+#define AWRESUMEEXAM_H
+
+#include <QWidget>
+
+namespace Ui {
+class awResumeExam;
+}
+
+class awResumeExam : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit awResumeExam(QWidget *parent = nullptr, QString sText = QString::fromLocal8Bit("正在进入断点续考…"));
+    ~awResumeExam();
+
+private:
+    void initUI();
+
+    Ui::awResumeExam *ui;
+};
+
+#endif // AWRESUMEEXAM_H

+ 52 - 0
client/awResumeExam.ui

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>awResumeExam</class>
+ <widget class="QWidget" name="awResumeExam">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>669</width>
+    <height>542</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_mask" native="true">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>50</y>
+     <width>491</width>
+     <height>161</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_awre_BG" native="true">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>30</y>
+      <width>321</width>
+      <height>71</height>
+     </rect>
+    </property>
+    <widget class="QLabel" name="label_awhp_title">
+     <property name="geometry">
+      <rect>
+       <x>120</x>
+       <y>30</y>
+       <width>141</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>正在进入断点续考…</string>
+     </property>
+    </widget>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 227 - 0
client/awWaitExam.cpp

@@ -0,0 +1,227 @@
+ #include "awWaitExam.h"
+#include "ui_awWaitExam.h"
+
+#include "CAppInfo.h"
+#include "awMsgBox.h"
+
+#include <QListWidgetItem>
+#include <QHBoxLayout>
+#include <QLabel>
+
+awWaitExam::awWaitExam(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::awWaitExam)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	qRegisterMetaType<CGetExamProperty>("CGetExamProperty");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetExamProperty, this, &awWaitExam::onGetExamProperty);
+	qRegisterMetaType<CGetExamRecordPaperStruct>("CGetExamRecordPaperStruct");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetExamRecordPaperStruct, this, &awWaitExam::onGetExamRecordPaperStruct);
+	qRegisterMetaType<CStartExam>("CStartExam");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnStartExam, this, &awWaitExam::onStartExam);
+
+
+	CHttpRequestPackage hrp;
+	hrp.sUri = QString("/api/ecs_exam_work/exam/getExamPropertyFromCacheByStudentSession/%1/%2")
+		.arg(g_appInfoPtr->m_oExamInfo.nExamId).arg("BEFORE_EXAM_REMARK");
+	hrp.nRequestType = RequestType::rtGetExamProperty;
+	hrp.sCommonStr = "BEFORE_EXAM_REMARK";
+	g_httpBllPtr->get(hrp);
+
+	CHttpRequestPackage hrp1;
+	hrp1.sUri = "/api/ecs_oe_student/client/exam/process/startExam";
+	hrp1.nRequestType = RequestType::rtStartExam;
+	hrp1.sParamList.push_back(QString("examStudentId,%1").arg(g_appInfoPtr->m_oExamInfo.nExamStudentId));
+	hrp1.eParamType = HttpParamType::hptUrl;
+	g_httpBllPtr->post(hrp1);
+
+	bHasStruct = false;
+	nCountDownCount = 60 * 2;
+	m_pTimer = std::make_shared<QTimer>();
+	m_pTimer->setInterval(1000);
+	m_pTimer->start();
+	connect(m_pTimer.get(), &QTimer::timeout, this, [&]() {
+		--nCountDownCount;
+		ui->btn_we_startExam->setText(QString::fromLocal8Bit("接受以上条款,开始考试(倒计时:%1:%2:%3)")
+			.arg(nCountDownCount / 3600, 2, 10, QChar('0'))
+			.arg((nCountDownCount % 3600) / 60, 2, 10, QChar('0'))
+			.arg(nCountDownCount % 60, 2, 10, QChar('0')));
+				
+		if (bHasStruct && nCountDownCount < 110)
+		{
+			ui->btn_we_startExam->setStyleSheet(QString(R"( QPushButton
+														{
+															background:rgba(19,187,138,1);
+															border-radius:%1px;
+															font-size:%2px;
+															font-family:"Microsoft YaHei";
+															font-weight:600;
+															color:rgba(255,255,255,0.5);
+														})").arg((int)(g_appInfoPtr->m_fRate*10))
+															.arg((int)(g_appInfoPtr->m_fRate * 14)));
+		}
+
+		if (nCountDownCount == 0)
+		{
+			m_pTimer->stop();
+			emit enterExam();
+			return;
+		}
+	});
+}
+
+awWaitExam::~awWaitExam()
+{
+    delete ui;
+}
+
+void awWaitExam::setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight)
+{
+    setGeometry(nLeft, nTop, nWidth, nHeight);
+    ui->widget_we_preNotice->setGeometry(0, 0, width()*970/(970+270), height());
+    ui->widget_we_paperStruct->setGeometry(ui->widget_we_preNotice->x() + ui->widget_we_preNotice->width() + g_appInfoPtr->m_fRate*20, 0,
+                                           width() - ui->widget_we_preNotice->width() - g_appInfoPtr->m_fRate*20, height());
+    ui->label_we_notice->adjustSize();
+    ui->label_we_notice->setGeometry(g_appInfoPtr->m_fRate*40, g_appInfoPtr->m_fRate*30,
+                                     ui->label_we_notice->width(), ui->label_we_notice->height());
+    ui->txtb_we_prenotice->setGeometry(ui->label_we_notice->x(), ui->label_we_notice->y() + ui->label_we_notice->height() + g_appInfoPtr->m_fRate*20,
+                                       ui->widget_we_preNotice->width() - g_appInfoPtr->m_fRate*40*2, g_appInfoPtr->m_fRate*390);
+    ui->btn_we_startExam->setGeometry((ui->widget_we_preNotice->width() - g_appInfoPtr->m_fRate*380)/2,
+                                      ui->txtb_we_prenotice->y() + ui->txtb_we_prenotice->height() + g_appInfoPtr->m_fRate*110,
+                                      g_appInfoPtr->m_fRate*380, g_appInfoPtr->m_fRate*40);
+    ui->label_we_struct->adjustSize();
+    ui->label_we_struct->setGeometry(g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*20,
+                                     ui->label_we_struct->width(), ui->label_we_struct->height());
+    ui->label_we_totalScore->adjustSize();
+    ui->label_we_totalScore->setGeometry(ui->widget_we_paperStruct->width() - g_appInfoPtr->m_fRate*30 - ui->label_we_totalScore->width(),
+                                         ui->label_we_struct->y() + (ui->label_we_struct->height() - ui->label_we_totalScore->height())/2,
+                                         ui->label_we_totalScore->width(), ui->label_we_totalScore->height());
+    ui->lw_we_struct->setGeometry(ui->label_we_struct->x(), ui->label_we_struct->y() + ui->label_we_struct->height() + g_appInfoPtr->m_fRate*20,
+                                  ui->widget_we_paperStruct->width() - g_appInfoPtr->m_fRate*30*2,
+                                  ui->widget_we_paperStruct->height() - ui->label_we_struct->y() - ui->label_we_struct->height() - g_appInfoPtr->m_fRate*(20 + 30));
+    
+}
+
+void awWaitExam::onStartExam(CStartExam startExam)
+{
+	if (startExam.nCode == 200)
+	{
+		g_appInfoPtr->m_oExamInfo.nExamRecordDataId = startExam.nExamRecordDataId;
+		g_appInfoPtr->m_oExamInfo.sCourseCode = startExam.sCourseCode;
+		g_appInfoPtr->m_oExamInfo.sCourseName = startExam.sCourseName;
+		g_appInfoPtr->m_oExamInfo.nFaceVerifyMinute = startExam.nFaceVerifyMinute;
+		CHttpRequestPackage hrp1;
+		hrp1.sUri = "/api/ecs_oe_student/client/exam/process/getExamRecordPaperStruct";
+		hrp1.sParamList.push_back(QString("examRecordDataId,%1").arg(g_appInfoPtr->m_oExamInfo.nExamRecordDataId));
+		hrp1.nRequestType = RequestType::rtGetExamRecordPaperStruct;
+		hrp1.eParamType = HttpParamType::hptUrl;
+		g_httpBllPtr->post(hrp1);
+	}
+	else
+	{
+        if(startExam.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("开始考试失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(startExam.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+
+
+void awWaitExam::onGetExamRecordPaperStruct(CGetExamRecordPaperStruct getExamRecordPaperStruct)
+{
+	if (getExamRecordPaperStruct.nCode == 200)
+	{
+		int nSize = getExamRecordPaperStruct.vPaperGroupStruct.size();
+		double tTotalScore = 0;
+		for (int i = 0; i < nSize; ++i)
+		{
+			CPaperGroupStruct pgs = getExamRecordPaperStruct.vPaperGroupStruct[i];
+			QListWidgetItem *pItem = new QListWidgetItem;
+			QSize size = pItem->sizeHint();
+			size.setHeight(g_appInfoPtr->m_fRate * 37);
+			pItem->setSizeHint(size);
+			ui->lw_we_struct->addItem(pItem);
+			QWidget *w = new QWidget;
+			//        w->setStyleSheet(QString("QWidget{background:rgba(19, 187, 138, 1); border-radius:%1px}").arg((int)(g_appInfoPtr->m_fRate*10)));
+			QHBoxLayout *l = new QHBoxLayout;
+			QString sLabelSubjectStyle = QString(R"(QLabel{
+                                      font-size:%1px;
+                                      font-family:"Microsoft YaHei";
+                                      font-weight:500;
+                                      color:rgba(68,68,68,1);})").arg((int)(g_appInfoPtr->m_fRate * 12));
+			QString sLabelSubjectInfoStyle = QString(R"(QLabel{
+                                      font-size:%1px;
+                                      font-family:"Microsoft YaHei";
+                                      font-weight:500;
+                                      color:rgba(153,153,153,1);})").arg((int)(g_appInfoPtr->m_fRate * 12));
+			QLabel *label_subject = new QLabel;
+			label_subject->setText(QString::fromLocal8Bit("%1、%2").arg(i+1).arg(pgs.sGroupName));
+			label_subject->setStyleSheet(sLabelSubjectStyle);
+			label_subject->adjustSize();
+			QLabel *label_subjectInfo = new QLabel;
+			
+			label_subjectInfo->setText(QString::fromLocal8Bit("共%1题,共%2分").arg(pgs.nTotalSubquestion).arg(pgs.fGroupScore));
+			label_subjectInfo->setStyleSheet(sLabelSubjectInfoStyle);
+			l->setContentsMargins(0, 0, 0, 0);
+			l->addWidget(label_subject);
+			l->addStretch();
+			l->addWidget(label_subjectInfo);
+			w->setLayout(l);
+			ui->lw_we_struct->setItemWidget(pItem, w);
+
+			tTotalScore += pgs.fGroupScore;
+		}
+		ui->label_we_totalScore->setText(QString::fromLocal8Bit("总分:%1").arg(tTotalScore));
+		bHasStruct = true;
+	}
+	else
+	{
+        if(getExamRecordPaperStruct.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取试卷结构失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(getExamRecordPaperStruct.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+void awWaitExam::onGetExamProperty(CGetExamProperty getExamProperty)
+{
+	if (getExamProperty.nCode == 200)
+	{
+		if (getExamProperty.sType == "BEFORE_EXAM_REMARK")
+		{
+			ui->txtb_we_prenotice->insertHtml(getExamProperty.sBeforeExamRemark);
+		}
+	}
+	else
+	{
+        if(getExamProperty.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取考试信息失败"), g_appInfoPtr->m_pAnsBgWidget);
+        }
+        else
+        {
+            ShowMsg(getExamProperty.sMessage, g_appInfoPtr->m_pAnsBgWidget);
+        }
+	}
+}
+
+void awWaitExam::on_btn_we_startExam_clicked()
+{
+	if (bHasStruct && nCountDownCount < 110)
+	{
+		m_pTimer->stop();
+		emit enterExam();
+	}    
+}

+ 37 - 0
client/awWaitExam.h

@@ -0,0 +1,37 @@
+#ifndef AWWAITEXAM_H
+#define AWWAITEXAM_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+#include <QTimer>
+
+namespace Ui {
+class awWaitExam;
+}
+
+class awWaitExam : public QWidget
+{
+    Q_OBJECT
+
+signals:
+    void enterExam();
+public:
+    explicit awWaitExam(QWidget *parent = nullptr);
+    ~awWaitExam();
+
+    void setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight);
+private slots:
+	void onStartExam(CStartExam startExam);
+	void onGetExamProperty(CGetExamProperty getExamProperty);
+	void onGetExamRecordPaperStruct(CGetExamRecordPaperStruct getExamRecordPaperStruct);
+    void on_btn_we_startExam_clicked();
+
+private:
+    Ui::awWaitExam *ui;
+	std::shared_ptr<QTimer> m_pTimer;
+
+	int nCountDownCount;
+	bool bHasStruct;
+};
+
+#endif // AWWAITEXAM_H

+ 111 - 0
client/awWaitExam.ui

@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>awWaitExam</class>
+ <widget class="QWidget" name="awWaitExam">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>942</width>
+    <height>717</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_we_preNotice" native="true">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>20</y>
+     <width>521</width>
+     <height>471</height>
+    </rect>
+   </property>
+   <widget class="QLabel" name="label_we_notice">
+    <property name="geometry">
+     <rect>
+      <x>30</x>
+      <y>50</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>考试说明</string>
+    </property>
+   </widget>
+   <widget class="QTextBrowser" name="txtb_we_prenotice">
+    <property name="geometry">
+     <rect>
+      <x>20</x>
+      <y>90</y>
+      <width>461</width>
+      <height>192</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_we_startExam">
+    <property name="geometry">
+     <rect>
+      <x>170</x>
+      <y>340</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>接受以上条款,开始考试(倒计时:00:04:50)</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QWidget" name="widget_we_paperStruct" native="true">
+   <property name="geometry">
+    <rect>
+     <x>550</x>
+     <y>20</y>
+     <width>291</width>
+     <height>501</height>
+    </rect>
+   </property>
+   <widget class="QLabel" name="label_we_struct">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>30</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>试卷概览</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_we_totalScore">
+    <property name="geometry">
+     <rect>
+      <x>150</x>
+      <y>30</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>总分:100</string>
+    </property>
+   </widget>
+   <widget class="QListWidget" name="lw_we_struct">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>90</y>
+      <width>256</width>
+      <height>192</height>
+     </rect>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 112 - 0
client/awqn_item.cpp

@@ -0,0 +1,112 @@
+#include "awqn_item.h"
+#include "ui_awqn_item.h"
+
+#include "CAppInfo.h"
+
+awqn_item::awqn_item(CSubQuestionStruct &sqs, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::awqn_item), m_sqs(sqs)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	ui->btn_awqn_quesion->setText(QString::number(sqs.nShowNumber));
+    refreshStatus();
+}
+
+awqn_item::~awqn_item()
+{
+    delete ui;
+}
+
+void awqn_item::setUI()
+{
+	ui->btn_awqn_quesion->setGeometry(0, 0, width(), height());
+
+}
+
+void awqn_item::on_btn_awqn_quesion_clicked()
+{
+    emit showSubQuestion(m_sqs.nOrder);
+}
+
+bool awqn_item::isMarked()
+{
+   return m_sqs.bMarked;
+}
+
+bool awqn_item::isAnswered()
+{
+    return m_sqs.bAnswered;
+}
+
+
+void awqn_item::refreshStatus()
+{
+    QString sNormalStyle = QString(R"(QPushButton
+        {
+            outline:none;
+            font-size:%1px;
+            font-family:"Microsoft YaHei";
+            font-weight:400;
+            color:rgba(136,146,146,1);
+            background:rgba(239,240,245,1);
+            border:0px;
+            border-radius:%2px;
+        })").arg((int)(g_appInfoPtr->m_fRate*12)).arg((int)(g_appInfoPtr->m_fRate*15));
+    QString sMarkedStyle = QString(R"(QPushButton
+        {
+            outline:none;
+            font-size:%1px;
+            font-family:"Microsoft YaHei";
+            font-weight:400;
+            color:rgba(255,159,67,1);
+            background:rgba(255,255,255,1);
+            border:%2px solid rgba(255,159,67,1);
+            border-radius:%3px;
+        })").arg((int)(g_appInfoPtr->m_fRate*12))
+            .arg((int)(g_appInfoPtr->m_fRate*1))
+            .arg((int)(g_appInfoPtr->m_fRate*15));
+    QString sAnsweredStyle = QString(R"(QPushButton
+        {
+            outline:none;
+            font-size:%1px;
+            font-family:"Microsoft YaHei";
+            font-weight:400;
+            color:rgba(131,172,247,1);
+            background:rgba(255,255,255,1);
+            border:%2px solid rgba(131,172,247,1);
+            border-radius:%3px;
+        })").arg((int)(g_appInfoPtr->m_fRate*12))
+            .arg((int)(g_appInfoPtr->m_fRate*1))
+            .arg((int)(g_appInfoPtr->m_fRate*15));
+    QString sCurrentStyle = QString(R"(QPushButton
+        {
+            outline:none;
+            font-size:%1px;
+            font-family:"Microsoft YaHei";
+            font-weight:400;
+            color:rgba(255,255,255,1);
+            background:rgba(19,187,138,1);
+            border:0px solid rgba(131,172,247,1);
+            border-radius:%2px;
+        })").arg((int)(g_appInfoPtr->m_fRate*12))
+            .arg((int)(g_appInfoPtr->m_fRate*15));
+    ui->btn_awqn_quesion->setStyleSheet(sNormalStyle);
+
+    if(m_sqs.bAnswered)
+    {
+        ui->btn_awqn_quesion->setStyleSheet(sAnsweredStyle);
+    }
+    
+	if(m_sqs.bMarked)
+    {
+        ui->btn_awqn_quesion->setStyleSheet(sMarkedStyle);
+    }
+    
+	if(m_sqs.bCurrent)
+    {
+        ui->btn_awqn_quesion->setStyleSheet(sCurrentStyle);
+    }
+}

+ 31 - 0
client/awqn_item.h

@@ -0,0 +1,31 @@
+#ifndef AWQN_ITEM_H
+#define AWQN_ITEM_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+
+namespace Ui {
+class awqn_item;
+}
+
+class awqn_item : public QWidget
+{
+    Q_OBJECT
+signals:
+    void showSubQuestion(int nOrder);
+public:
+    explicit awqn_item(CSubQuestionStruct &sqs, QWidget *parent = nullptr);
+    ~awqn_item();
+	void setUI();
+    void refreshStatus();
+    bool isMarked();
+    bool isAnswered();
+private slots:
+    void on_btn_awqn_quesion_clicked();
+
+private:
+    Ui::awqn_item *ui;
+    CSubQuestionStruct &m_sqs;
+};
+
+#endif // AWQN_ITEM_H

+ 32 - 0
client/awqn_item.ui

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>awqn_item</class>
+ <widget class="QWidget" name="awqn_item">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>87</width>
+    <height>50</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QPushButton" name="btn_awqn_quesion">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>10</y>
+     <width>21</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>1</string>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 92 - 0
client/clEditPassword.cpp

@@ -0,0 +1,92 @@
+#include "clEditPassword.h"
+#include "ui_clEditPassword.h"
+
+#include <QValidator>
+#include "CAppInfo.h"
+#include "logproc.h"
+#include "awMsgBox.h"
+
+clEditPassword::clEditPassword(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clEditPassword)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	qRegisterMetaType<CEditPassword>("CEditPassword");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnEditPassword, this, &clEditPassword::onEditPassword);
+}
+
+clEditPassword::~clEditPassword()
+{
+    delete ui;
+}
+
+
+void clEditPassword::setUI(const int nWidth, const int nHeight)
+{
+    setGeometry(0, 0, nWidth, nHeight);
+	QRegExp regx("[a-zA-Z0-9]+$");	
+    ui->edt_oldPassword->setGeometry(g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*30,
+                                     g_appInfoPtr->m_fRate*360, g_appInfoPtr->m_fRate*40);
+	QValidator *validator = new QRegExpValidator(regx, ui->edt_newPassword);
+	ui->edt_newPassword->setValidator(validator);
+	ui->edt_newPassword->setGeometry(ui->edt_oldPassword->x(), ui->edt_oldPassword->y() + ui->edt_oldPassword->height() + g_appInfoPtr->m_fRate*20,
+                                     ui->edt_oldPassword->width(), ui->edt_oldPassword->height());
+	QValidator *validator1 = new QRegExpValidator(regx, ui->edt_newPassword1);
+	ui->edt_newPassword1->setValidator(validator1);
+    ui->edt_newPassword1->setGeometry(ui->edt_oldPassword->x(), ui->edt_newPassword->y() + ui->edt_newPassword->height() + g_appInfoPtr->m_fRate*20,
+                                    ui->edt_oldPassword->width(), ui->edt_oldPassword->height());
+    ui->btn_savePassword->setGeometry(ui->edt_oldPassword->x(), ui->edt_newPassword1->y() + ui->edt_newPassword1->height() + g_appInfoPtr->m_fRate*20,
+                                      g_appInfoPtr->m_fRate*80, g_appInfoPtr->m_fRate*36);
+}
+
+void clEditPassword::on_btn_savePassword_clicked()
+{
+    if (ui->edt_oldPassword->text().isEmpty())
+	{
+        ShowMsg(QString::fromLocal8Bit("请先输入原始密码"), this);
+        return;
+	}
+
+    if (ui->edt_newPassword->text() != ui->edt_newPassword1->text())
+    {
+        ShowMsg(QString::fromLocal8Bit("两次输入密码不一致"), this);
+        return;
+    }
+
+    if(ui->edt_newPassword->text().length() < 6 ||
+            ui->edt_newPassword->text().length() > 18)
+    {
+        ShowMsg(QString::fromLocal8Bit("请输入6到18位数字或字母"), this);
+        return;
+    }
+
+    CHttpRequestPackage hrp;
+    hrp.sUri = "/api/ecs_core/student/password";
+    hrp.nRequestType = RequestType::rtEditPassword;
+    hrp.eParamType = HttpParamType::hptUrl;
+	hrp.sParamList.push_back(QString("newPassword,%1").arg(ui->edt_newPassword->text()));
+	hrp.sParamList.push_back(QString("password,%1").arg(ui->edt_oldPassword->text()));    
+    g_httpBllPtr->put(hrp);
+}
+
+void clEditPassword::onEditPassword(CEditPassword editPassword)
+{
+	if (editPassword.nCode == 200)
+	{
+		ShowMsg(QString::fromLocal8Bit("保存密码成功"), this);
+	}
+	else
+	{
+		if (editPassword.sMessage != "")
+		{
+			ShowMsg(editPassword.sMessage, this);
+		}
+		else
+		{
+			ShowMsg(QString::fromLocal8Bit("保存密码失败"), this);
+		}
+	}
+}

+ 28 - 0
client/clEditPassword.h

@@ -0,0 +1,28 @@
+#ifndef CLEDITPASSWORD_H
+#define CLEDITPASSWORD_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+
+namespace Ui {
+class clEditPassword;
+}
+
+class clEditPassword : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit clEditPassword(QWidget *parent = nullptr);
+    ~clEditPassword();
+
+    void setUI(const int nWidth, const int nHeight);
+private slots:
+    void on_btn_savePassword_clicked();
+	
+	void onEditPassword(CEditPassword editPassword);
+private:
+    Ui::clEditPassword *ui;
+};
+
+#endif // CLEDITPASSWORD_H

+ 80 - 0
client/clEditPassword.ui

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clEditPassword</class>
+ <widget class="QWidget" name="clEditPassword">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>587</width>
+    <height>407</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QLineEdit" name="edt_oldPassword">
+   <property name="geometry">
+    <rect>
+     <x>70</x>
+     <y>40</y>
+     <width>113</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="echoMode">
+    <enum>QLineEdit::Password</enum>
+   </property>
+   <property name="placeholderText">
+    <string>请输入旧密码</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="edt_newPassword">
+   <property name="geometry">
+    <rect>
+     <x>70</x>
+     <y>90</y>
+     <width>113</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="echoMode">
+    <enum>QLineEdit::Password</enum>
+   </property>
+   <property name="placeholderText">
+    <string>请输入新密码(6到18位数字或字母)</string>
+   </property>
+  </widget>
+  <widget class="QLineEdit" name="edt_newPassword1">
+   <property name="geometry">
+    <rect>
+     <x>70</x>
+     <y>140</y>
+     <width>113</width>
+     <height>20</height>
+    </rect>
+   </property>
+   <property name="echoMode">
+    <enum>QLineEdit::Password</enum>
+   </property>
+   <property name="placeholderText">
+    <string>请再次输入新密码</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="btn_savePassword">
+   <property name="geometry">
+    <rect>
+     <x>70</x>
+     <y>200</y>
+     <width>75</width>
+     <height>23</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>保存</string>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 113 - 0
client/clMobileLogin.cpp

@@ -0,0 +1,113 @@
+#include "clMobileLogin.h"
+#include "ui_clMobileLogin.h"
+
+#include "CAppInfo.h"
+#include "CQREncode.h"
+
+#include <QGraphicsDropShadowEffect>
+
+clMobileLogin::clMobileLogin(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clMobileLogin)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+}
+
+clMobileLogin::~clMobileLogin()
+{
+    delete ui;
+}
+
+void clMobileLogin::setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight)
+{
+    setGeometry(nLeft, nTop, nWidth, nHeight);
+    ui->widget_npw_BG->setGeometry(g_appInfoPtr->m_fRate*5, g_appInfoPtr->m_fRate*5, width()-g_appInfoPtr->m_fRate*10, height()-g_appInfoPtr->m_fRate*10);
+    ui->btn_downloadApp->setGeometry(g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*20,
+                                     g_appInfoPtr->m_fRate*100, g_appInfoPtr->m_fRate*32);
+    ui->btn_bindUser->setGeometry(ui->btn_downloadApp->x() + ui->btn_downloadApp->width(),
+                                  ui->btn_downloadApp->y(), ui->btn_downloadApp->width(), ui->btn_downloadApp->height());
+    ui->label_qrcode->setGeometry((ui->widget_npw_BG->width() - g_appInfoPtr->m_fRate*164)/2, ui->btn_downloadApp->y() + ui->btn_downloadApp->height() + g_appInfoPtr->m_fRate*20,
+                                  g_appInfoPtr->m_fRate*164, g_appInfoPtr->m_fRate*164);
+    ui->label_useAppScan->adjustSize();
+    ui->label_useAppScan->setGeometry((ui->widget_npw_BG->width() - ui->label_useAppScan->width())/2,
+                                      ui->label_qrcode->y() + ui->label_qrcode->height() + g_appInfoPtr->m_fRate*10,
+                                      ui->label_useAppScan->width(), ui->label_useAppScan->height());
+
+    QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect;
+    effect->setOffset(g_appInfoPtr->m_fRate*4,g_appInfoPtr->m_fRate*4);
+    effect->setColor(QColor(0,0,0,30));
+    effect->setBlurRadius(g_appInfoPtr->m_fRate*16);
+    ui->widget_npw_BG->setGraphicsEffect(effect);
+
+    on_btn_downloadApp_clicked();
+}
+
+void clMobileLogin::setBtnDown(ML_BTN_TYPE type)
+{
+	QString sDownLoadAppStyle = QString(R"(QPushButton
+                                {
+                                    outline:none;
+                                    background:rgba(%1,1);
+                                    font-size:%2px;
+                                    font-family:"Microsoft YaHei";
+                                    font-weight:400;
+                                    color:rgba(%3,1);
+                                    border-top-left-radius:%4px;
+                                    border-bottom-left-radius:%5px;
+                                })");
+	QString sDownLoadAppNormalStyle = sDownLoadAppStyle.arg("244,243,247").arg((int)(g_appInfoPtr->m_fRate*12))
+                                .arg("102, 102, 102")
+								.arg((int)(g_appInfoPtr->m_fRate*6))
+                                .arg((int)(g_appInfoPtr->m_fRate*6));
+	QString sDownLoadAppCheckStyle = sDownLoadAppStyle.arg("19,187,138").arg((int)(g_appInfoPtr->m_fRate * 12))
+		.arg("255,255,255")
+		.arg((int)(g_appInfoPtr->m_fRate * 6))
+		.arg((int)(g_appInfoPtr->m_fRate * 6));
+
+	QString sBindUserStyle = QString(R"(QPushButton
+                                {
+                                    outline:none;
+                                    background:rgba(%1,1);
+                                    font-size:%2px;
+                                    font-family:"Microsoft YaHei";
+                                    font-weight:400;
+                                    color:rgba(%3,1);
+                                    border-top-right-radius:%4px;
+                                    border-bottom-right-radius:%5px;
+                                })");
+	QString sBindUserNormalStyle = sBindUserStyle.arg("244,243,247").arg((int)(g_appInfoPtr->m_fRate * 12))
+									.arg("102, 102, 102").arg((int)(g_appInfoPtr->m_fRate*6))
+                                    .arg((int)(g_appInfoPtr->m_fRate*6));
+	QString sBindUserCheckStyle = sBindUserStyle.arg("19,187,138").arg((int)(g_appInfoPtr->m_fRate * 12))
+		.arg("255,255,255").arg((int)(g_appInfoPtr->m_fRate * 6))
+		.arg((int)(g_appInfoPtr->m_fRate * 6));
+    if(type == ML_BTN_TYPE::mbt_downloadApp)
+    {
+        ui->btn_downloadApp->setStyleSheet(sDownLoadAppCheckStyle);
+        ui->btn_bindUser->setStyleSheet(sBindUserNormalStyle);
+    }
+    else
+    {
+        ui->btn_downloadApp->setStyleSheet(sDownLoadAppNormalStyle);
+        ui->btn_bindUser->setStyleSheet(sBindUserCheckStyle);
+    }
+
+}
+
+void clMobileLogin::on_btn_downloadApp_clicked()
+{
+    ui->label_qrcode->setPixmap(CQREncode::GenerateQRcode(g_appInfoPtr->m_sAppDownLoadUrl, ui->label_qrcode->width()));
+    ui->label_useAppScan->setVisible(false);
+    setBtnDown(ML_BTN_TYPE::mbt_downloadApp);
+}
+
+void clMobileLogin::on_btn_bindUser_clicked()
+{
+    QString sUserInfo = QString(R"({"rootOrgId":%1,"studentCode":"%2","identityNumber":"%3"})")
+            .arg(g_appInfoPtr->m_sRootOrgId).arg(g_appInfoPtr->m_sStudentCode).arg(g_appInfoPtr->m_sStudentIdentityNumber);
+    ui->label_qrcode->setPixmap(CQREncode::GenerateQRcode(sUserInfo, ui->label_qrcode->width()));
+    ui->label_useAppScan->setVisible(true);
+    setBtnDown(ML_BTN_TYPE::mbt_bindUser);
+}

+ 36 - 0
client/clMobileLogin.h

@@ -0,0 +1,36 @@
+#ifndef CLMOBILELOGIN_H
+#define CLMOBILELOGIN_H
+
+#include <QWidget>
+
+namespace Ui {
+class clMobileLogin;
+}
+
+enum class ML_BTN_TYPE
+{
+    mbt_downloadApp = 1,
+    mbt_bindUser
+};
+
+class clMobileLogin : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit clMobileLogin(QWidget *parent = nullptr);
+    ~clMobileLogin();
+
+    void setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight);
+private slots:
+    void on_btn_downloadApp_clicked();
+
+    void on_btn_bindUser_clicked();
+
+private:
+    void setBtnDown(ML_BTN_TYPE type);
+
+    Ui::clMobileLogin *ui;
+};
+
+#endif // CLMOBILELOGIN_H

+ 81 - 0
client/clMobileLogin.ui

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clMobileLogin</class>
+ <widget class="QWidget" name="clMobileLogin">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_npw_BG" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>-20</y>
+     <width>401</width>
+     <height>301</height>
+    </rect>
+   </property>
+   <widget class="QPushButton" name="btn_downloadApp">
+    <property name="geometry">
+     <rect>
+      <x>100</x>
+      <y>50</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>下载安卓apk</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_bindUser">
+    <property name="geometry">
+     <rect>
+      <x>180</x>
+      <y>50</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>绑定用户</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_useAppScan">
+    <property name="geometry">
+     <rect>
+      <x>80</x>
+      <y>260</y>
+      <width>191</width>
+      <height>21</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>请使用“云考”APP扫描</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_qrcode">
+    <property name="geometry">
+     <rect>
+      <x>90</x>
+      <y>80</y>
+      <width>191</width>
+      <height>141</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 50 - 0
client/clNoticeDetail.cpp

@@ -0,0 +1,50 @@
+#include "clNoticeDetail.h"
+#include "ui_clNoticeDetail.h"
+
+#include "CAppInfo.h"
+
+clNoticeDetail::clNoticeDetail(CNoticeInfo ni, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clNoticeDetail), m_ni(ni)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+}
+
+clNoticeDetail::~clNoticeDetail()
+{
+    delete ui;
+}
+
+void clNoticeDetail::setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight)
+{
+    setGeometry(nLeft, nTop, nWidth, nHeight);
+    ui->widget_offlineExam->setGeometry(0, 0, width(), height());
+    ui->widget_op_top->setGeometry(0, 0, width(), g_appInfoPtr->m_fRate*72);
+    ui->btn_gobackList->setGeometry(ui->widget_op_top->width() - g_appInfoPtr->m_fRate*30 - g_appInfoPtr->m_fRate*108,
+                                             (ui->widget_op_top->height() - g_appInfoPtr->m_fRate*32)/2,
+                                             g_appInfoPtr->m_fRate*108, g_appInfoPtr->m_fRate*32);
+    ui->btn_gobackList->setIconSize(QSize(g_appInfoPtr->m_fRate*14, g_appInfoPtr->m_fRate*14));
+    ui->btn_gobackList->setIcon(QIcon(":/images/icon-goback-list.png"));
+    ui->widget_clnd_client->setGeometry(0, ui->widget_op_top->height(), width(), height() - ui->widget_op_top->height());
+	
+	ui->label_clnd_title->setText(m_ni.sTitle);
+	ui->label_clnd_title->adjustSize();
+    ui->label_clnd_title->setGeometry((ui->widget_clnd_client->width() - ui->label_clnd_title->width())/2,
+                                      g_appInfoPtr->m_fRate*30, ui->label_clnd_title->width(), ui->label_clnd_title->height());
+	ui->label_clnd_time->setText(m_ni.sPublishTime);
+	ui->label_clnd_time->adjustSize();
+    ui->label_clnd_time->setGeometry((ui->widget_clnd_client->width() - ui->label_clnd_time->width())/2,
+                                     ui->label_clnd_title->y() + ui->label_clnd_title->height() + g_appInfoPtr->m_fRate*10, ui->label_clnd_time->width(), ui->label_clnd_time->height());
+    ui->label_HLine->setGeometry(g_appInfoPtr->m_fRate*30, ui->label_clnd_time->y() + ui->label_clnd_time->height() + g_appInfoPtr->m_fRate*30,
+                                 ui->widget_clnd_client->width() - g_appInfoPtr->m_fRate*30*2, g_appInfoPtr->m_fRate*1);
+	ui->txtb_noticeContent->insertHtml(m_ni.sContent);
+	ui->txtb_noticeContent->setGeometry(ui->label_HLine->x(), ui->label_HLine->y() + ui->label_HLine->height() + g_appInfoPtr->m_fRate*16,
+                                        ui->label_HLine->width(), ui->widget_clnd_client->height() - ui->label_HLine->y() - g_appInfoPtr->m_fRate*(16 + 16 + 1));
+}
+
+void clNoticeDetail::on_btn_gobackList_clicked()
+{
+	emit gobackNoticeList();
+}

+ 30 - 0
client/clNoticeDetail.h

@@ -0,0 +1,30 @@
+#ifndef CLNOTICEDETAIL_H
+#define CLNOTICEDETAIL_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+
+namespace Ui {
+class clNoticeDetail;
+}
+
+class clNoticeDetail : public QWidget
+{
+    Q_OBJECT
+signals:
+	void gobackNoticeList();
+public:
+    explicit clNoticeDetail(CNoticeInfo ni, QWidget *parent = nullptr);
+    ~clNoticeDetail();
+
+    void setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight);
+private slots:
+    void on_btn_gobackList_clicked();
+
+private:
+    Ui::clNoticeDetail *ui;
+
+	CNoticeInfo m_ni;
+};
+
+#endif // CLNOTICEDETAIL_H

+ 111 - 0
client/clNoticeDetail.ui

@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clNoticeDetail</class>
+ <widget class="QWidget" name="clNoticeDetail">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>722</width>
+    <height>607</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_offlineExam" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>20</y>
+     <width>871</width>
+     <height>571</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_op_top" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>931</width>
+      <height>80</height>
+     </rect>
+    </property>
+    <widget class="QPushButton" name="btn_gobackList">
+     <property name="geometry">
+      <rect>
+       <x>520</x>
+       <y>30</y>
+       <width>161</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>返回列表</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QWidget" name="widget_clnd_client" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>80</y>
+      <width>971</width>
+      <height>611</height>
+     </rect>
+    </property>
+    <widget class="QLabel" name="label_clnd_title">
+     <property name="geometry">
+      <rect>
+       <x>300</x>
+       <y>10</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>金融投资中的教学方法</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_clnd_time">
+     <property name="geometry">
+      <rect>
+       <x>290</x>
+       <y>40</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>2019-10-25 11:33:29</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_HLine">
+     <property name="geometry">
+      <rect>
+       <x>200</x>
+       <y>70</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QTextBrowser" name="txtb_noticeContent">
+     <property name="geometry">
+      <rect>
+       <x>180</x>
+       <y>130</y>
+       <width>361</width>
+       <height>171</height>
+      </rect>
+     </property>
+    </widget>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 193 - 0
client/clNoticeList.cpp

@@ -0,0 +1,193 @@
+#include "clNoticeList.h"
+#include "ui_clNoticeList.h"
+
+
+#include "CAppInfo.h"
+#include "clOperation.h"
+#include "CheckHeaderView.h"
+#include <QLabel>
+#include <QScrollBar>
+#include <QHBoxLayout>
+#include "logproc.h"
+
+clNoticeList::clNoticeList(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clNoticeList)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+}
+
+clNoticeList::~clNoticeList()
+{
+    delete ui;
+}
+
+void clNoticeList::setUI(const int nWidth, const int nHeight)
+{
+    setGeometry(0, 0, nWidth, nHeight);
+    ui->widget_noticelist->setGeometry(0, 0, width(), height());
+    ui->widget_op_top->setGeometry(0, 0, width(), g_appInfoPtr->m_fRate*72);
+    ui->btn_setRead->setGeometry(ui->widget_op_top->width() - g_appInfoPtr->m_fRate*30 - g_appInfoPtr->m_fRate*120,
+                                             (ui->widget_op_top->height() - g_appInfoPtr->m_fRate*32)/2,
+                                             g_appInfoPtr->m_fRate*120, g_appInfoPtr->m_fRate*32);
+    ui->btn_setRead->setIconSize(QSize(g_appInfoPtr->m_fRate*16, g_appInfoPtr->m_fRate*14));
+    ui->btn_setRead->setIcon(QIcon(":/images/btn-notice-set-read.png"));
+    ui->tablewt_examList->setGeometry(g_appInfoPtr->m_fRate*30, ui->widget_op_top->height() + g_appInfoPtr->m_fRate*10,
+                                      ui->widget_noticelist->width() - g_appInfoPtr->m_fRate*30*2,
+                                      ui->widget_noticelist->height() - ui->widget_op_top->height() - g_appInfoPtr->m_fRate*(10 + 62));
+    ui->tablewt_examList->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablewt_examList->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablewt_examList->setSelectionMode(QAbstractItemView::NoSelection);
+    //隐藏列表头
+    ui->tablewt_examList->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablewt_examList->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablewt_examList->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablewt_examList->setRowCount(10);
+    //设置列数
+    ui->tablewt_examList->setColumnCount(3);
+
+    ui->tablewt_examList->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablewt_examList->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+    ui->tablewt_examList->setIconSize(QSize(g_appInfoPtr->m_fRate*36, g_appInfoPtr->m_fRate*36));
+    QStringList sHeadStr;
+    sHeadStr << QString::fromLocal8Bit("") <<
+                QString::fromLocal8Bit("标题") <<
+                QString::fromLocal8Bit("发送时间");
+
+    //表头加入checkbox    
+    CheckHeaderView *CheckHead = new CheckHeaderView(0, Qt::Horizontal, ui->tablewt_examList);
+    connect(CheckHead, &CheckHeaderView::checkboxStateChanged, this, [&](int state){
+        for(int i = 0; i < ui->tablewt_examList->rowCount(); i++)
+        {
+            QCheckBox* chk = dynamic_cast<QCheckBox*>(ui->tablewt_examList->cellWidget(i, 0));
+            if(chk)
+            {              
+                chk->setCheckState((Qt::CheckState)state);
+            }
+        }
+    });
+    ui->tablewt_examList->setHorizontalHeader(CheckHead);
+    ui->tablewt_examList->setHorizontalHeaderLabels(sHeadStr);
+
+    ui->tablewt_examList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablewt_examList->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablewt_examList->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    ui->tablewt_examList->setColumnWidth(0, g_appInfoPtr->m_fRate*56);
+    ui->tablewt_examList->setColumnWidth(1, g_appInfoPtr->m_fRate*940);
+    ui->tablewt_examList->setColumnWidth(2, g_appInfoPtr->m_fRate*112);
+
+    ui->tablewt_examList->horizontalHeader()->setStretchLastSection(true);
+
+    
+}
+
+void clNoticeList::setNotice(std::vector<CNoticeInfo> vNL)
+{
+    QString sCheckStyle = QString(R"(QCheckBox
+                                  {
+                                      outline: none;
+                                      height:%1px;
+
+                                      font-size:%2px;
+                                      font-family:"Microsoft YaHei";
+                                      font-weight:400;
+                                      color:rgba(102,102,102,1);
+                                  }
+
+                                  QCheckBox::indicator
+                                  {
+                                      width:%3px;
+                                      height:%4px;
+                                  }
+
+                                  QCheckBox::indicator:unchecked
+                                  {
+                                      border: %5px solid rgba(228,229,235,1);
+                                      border-radius:%6px;
+                                      background:rgba(239,240,245,1);
+                                  }
+
+                                  QCheckBox::indicator:checked
+                                  {
+                                      border-image: url(:/images/icon-checkbox-checked.png);
+                                  })").arg((int)(g_appInfoPtr->m_fRate*26))
+                                      .arg((int)(g_appInfoPtr->m_fRate*14))
+                                      .arg((int)(g_appInfoPtr->m_fRate*14))
+                                      .arg((int)(g_appInfoPtr->m_fRate*14))
+                                      .arg((int)(g_appInfoPtr->m_fRate*1))
+                                      .arg((int)(g_appInfoPtr->m_fRate*3));
+	vNoticeList.clear();
+	vNoticeList.assign(vNL.begin(), vNL.end());
+	int nSize = vNoticeList.size();
+	for (int i = 0; i < nSize; ++i)
+	{
+		CNoticeInfo ni = vNoticeList[i];
+		QCheckBox *chkBox = new QCheckBox;
+        chkBox->setStyleSheet(sCheckStyle);
+		ui->tablewt_examList->setCellWidget(i, 0, chkBox);
+
+		QWidget *widget = new QWidget;		
+		QHBoxLayout *layout = new QHBoxLayout;
+		widget->setLayout(layout);
+		QLabel *icon = new QLabel;
+		icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 16, g_appInfoPtr->m_fRate * 12));
+		QString sIconFile = "";
+		if (ni.bHasRead)
+		{
+			sIconFile = ":/images/icon-notice-read.png";
+		}
+		else
+		{
+			sIconFile = ":/images/icon-notice-unread.png";
+		}
+		icon->setPixmap(QPixmap(sIconFile).scaled(g_appInfoPtr->m_fRate * 16, g_appInfoPtr->m_fRate * 12));
+		layout->addWidget(icon);
+		QMyLabel *text = new QMyLabel(i);
+		connect(text, &QMyLabel::sgnClick, this, &clNoticeList::on_tablewt_examList_cellClicked);
+		text->setText(ni.sContent);
+		text->setFixedWidth(900);
+		text->setStyleSheet(QString(R"(QLabel
+                                    {
+                                        font-size:%1px;
+                                        font-family:"Microsoft YaHei";
+                                        font-weight:400;
+                                        color:rgba(51,51,51,1);
+                                    })").arg((int)(g_appInfoPtr->m_fRate * 12)));
+		layout->addWidget(text);
+		layout->addStretch();
+
+		ui->tablewt_examList->setCellWidget(i, 1, widget);
+		ui->tablewt_examList->setItem(i, 2, new QTableWidgetItem(ni.sPublishTime));
+
+	}
+}
+
+void clNoticeList::on_tablewt_examList_cellClicked(int row, int column)
+{
+    if ((int)(vNoticeList.size() - 1) < row ||
+            column == 0)
+	{
+		return;
+	}
+
+	if (m_pNoticeDetail == nullptr)
+	{
+		m_pNoticeDetail = std::make_shared<clNoticeDetail>(vNoticeList[row], this);
+		m_pNoticeDetail->setUI(0, 0, width(), height());
+		connect(m_pNoticeDetail.get(), &clNoticeDetail::gobackNoticeList, this, [&]() {
+			if (m_pNoticeDetail != nullptr)
+			{
+				m_pNoticeDetail.reset();
+			}
+		});
+	}
+	m_pNoticeDetail->show();
+}

+ 44 - 0
client/clNoticeList.h

@@ -0,0 +1,44 @@
+#ifndef CLNOTICELIST_H
+#define CLNOTICELIST_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+#include "clNoticeDetail.h"
+#include <QLabel>
+namespace Ui {
+class clNoticeList;
+}
+
+class QMyLabel : public QLabel
+{
+	Q_OBJECT
+signals:
+	void sgnClick(int nRow, int nCol);
+public:
+	explicit QMyLabel(int nRow, QWidget *parent = nullptr) :QLabel(parent),m_nRow(nRow){  }
+private:
+	void mouseReleaseEvent(QMouseEvent *ev) {emit sgnClick(m_nRow, 1); }
+	int m_nRow;
+};
+class clNoticeList : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit clNoticeList(QWidget *parent = nullptr);
+    ~clNoticeList();
+
+    void setUI(const int nWidth, const int nHeight);
+
+	void setNotice(std::vector<CNoticeInfo> vNL);
+private slots:
+    void on_tablewt_examList_cellClicked(int row, int column);
+
+private:
+    Ui::clNoticeList *ui;
+
+	std::vector<CNoticeInfo> vNoticeList;
+	std::shared_ptr<clNoticeDetail> m_pNoticeDetail;
+};
+
+#endif // CLNOTICELIST_H

+ 62 - 0
client/clNoticeList.ui

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clNoticeList</class>
+ <widget class="QWidget" name="clNoticeList">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>888</width>
+    <height>542</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_noticelist" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>30</y>
+     <width>871</width>
+     <height>571</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_op_top" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>931</width>
+      <height>80</height>
+     </rect>
+    </property>
+    <widget class="QPushButton" name="btn_setRead">
+     <property name="geometry">
+      <rect>
+       <x>604</x>
+       <y>30</y>
+       <width>161</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>标记为已读</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QTableWidget" name="tablewt_examList">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>80</y>
+      <width>871</width>
+      <height>401</height>
+     </rect>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 55 - 0
client/clNoticePopWidget.cpp

@@ -0,0 +1,55 @@
+#include "clNoticePopWidget.h"
+#include "ui_clNoticePopWidget.h"
+
+#include "CAppInfo.h"
+
+#include <QGraphicsDropShadowEffect>
+
+clNoticePopWidget::clNoticePopWidget(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clNoticePopWidget)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+}
+
+clNoticePopWidget::~clNoticePopWidget()
+{
+    delete ui;
+}
+
+void clNoticePopWidget::setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight)
+{
+    setGeometry(nLeft, nTop, nWidth, nHeight);
+    ui->widget_npw_BG->setGeometry(g_appInfoPtr->m_fRate*5, g_appInfoPtr->m_fRate*5, width()-g_appInfoPtr->m_fRate*10, height()-g_appInfoPtr->m_fRate*10);
+    ui->label_noticeIcon->setGeometry(g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*24,
+                                      g_appInfoPtr->m_fRate*16, g_appInfoPtr->m_fRate*12);
+    ui->label_noticeTitle->adjustSize();
+    ui->label_noticeTitle->setGeometry(ui->label_noticeIcon->x() + ui->label_noticeIcon->width() + g_appInfoPtr->m_fRate*8,
+                                       ui->label_noticeIcon->y() + (ui->label_noticeIcon->height() - ui->label_noticeTitle->height())/2,
+                                       ui->label_noticeTitle->width(), ui->label_noticeTitle->height());
+    ui->txtb_noticeContent->setGeometry(ui->label_noticeIcon->x(), ui->label_noticeTitle->y() + ui->label_noticeTitle->height() + g_appInfoPtr->m_fRate*10,
+                                        g_appInfoPtr->m_fRate*260, g_appInfoPtr->m_fRate*60);
+    ui->btn_detail->setGeometry(ui->widget_npw_BG->width() - g_appInfoPtr->m_fRate*(20 + 80),
+                                ui->widget_npw_BG->height() - g_appInfoPtr->m_fRate*(20 + 30),
+                                g_appInfoPtr->m_fRate*80, g_appInfoPtr->m_fRate*30);
+    ui->btn_ignore->setGeometry(ui->btn_detail->x() - g_appInfoPtr->m_fRate*10 - ui->btn_detail->width(), ui->btn_detail->y(),
+                                ui->btn_detail->width(), ui->btn_detail->height());
+
+    QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect;
+    effect->setOffset(g_appInfoPtr->m_fRate*4,g_appInfoPtr->m_fRate*4);
+    effect->setColor(QColor(0,0,0,30));
+    effect->setBlurRadius(g_appInfoPtr->m_fRate*16);
+    ui->widget_npw_BG->setGraphicsEffect(effect);
+}
+
+void clNoticePopWidget::addNotice(const CNoticeInfo ni)
+{
+	vNL.push_back(ni);
+}
+
+int clNoticePopWidget::noticeCount()
+{
+	return vNL.size();
+}

+ 28 - 0
client/clNoticePopWidget.h

@@ -0,0 +1,28 @@
+#ifndef CLNOTICEPOPWIDGET_H
+#define CLNOTICEPOPWIDGET
+
+#include <QWidget>
+#include "CHttpBll.h"
+
+namespace Ui {
+class clNoticePopWidget;
+}
+
+class clNoticePopWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit clNoticePopWidget(QWidget *parent = nullptr);
+    ~clNoticePopWidget();
+
+    void setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight);
+
+	void addNotice(const CNoticeInfo ni);
+	int noticeCount();
+private:
+    Ui::clNoticePopWidget *ui;
+	std::vector<CNoticeInfo> vNL;
+};
+
+#endif // CLNOTICEPOPWIDGET_H

+ 91 - 0
client/clNoticePopWidget.ui

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clNoticePopWidget</class>
+ <widget class="QWidget" name="clNoticePopWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_npw_BG" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>401</width>
+     <height>301</height>
+    </rect>
+   </property>
+   <widget class="QLabel" name="label_noticeIcon">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>20</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_noticeTitle">
+    <property name="geometry">
+     <rect>
+      <x>70</x>
+      <y>20</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>公告通知大标题</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_detail">
+    <property name="geometry">
+     <rect>
+      <x>300</x>
+      <y>260</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>详情</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_ignore">
+    <property name="geometry">
+     <rect>
+      <x>200</x>
+      <y>260</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>忽略</string>
+    </property>
+   </widget>
+   <widget class="QTextBrowser" name="txtb_noticeContent">
+    <property name="geometry">
+     <rect>
+      <x>20</x>
+      <y>40</y>
+      <width>361</width>
+      <height>171</height>
+     </rect>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 227 - 0
client/clObjectiveScore.cpp

@@ -0,0 +1,227 @@
+#include "clObjectiveScore.h"
+#include "ui_clObjectiveScore.h"
+
+#include "CAppInfo.h"
+
+#include <QListWidgetItem>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QGraphicsDropShadowEffect>
+#include "logproc.h"
+#include "awMsgBox.h"
+
+clObjectiveScore::clObjectiveScore(int nRow, QString sExamStudentId, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clObjectiveScore), m_sExamStudentId(sExamStudentId), m_nRow(nRow)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	qRegisterMetaType<CQueryObjectiveScoreList>("CQueryObjectiveScoreList");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnQueryObjectiveScoreList, this, &clObjectiveScore::onQueryObjectiveScoreList);
+
+	CHttpRequestPackage hrp;
+	hrp.sUri = "/api/ecs_oe_admin/client/exam/process/queryObjectiveScoreList";
+	hrp.nRequestType = RequestType::rtQueryObjectiveScoreList;
+	hrp.eParamType = HttpParamType::hptUrl;
+	hrp.sCommonStr = sExamStudentId;
+	hrp.sParamList.push_back(QString("examStudentId,%1").arg(m_sExamStudentId));
+	g_httpBllPtr->post(hrp);
+}
+
+clObjectiveScore::~clObjectiveScore()
+{
+    delete ui;
+}
+
+void clObjectiveScore::setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight)
+{
+    setGeometry(nLeft, nTop, nWidth, nHeight);
+    ui->widget_objevtiveScore->setGeometry(g_appInfoPtr->m_fRate*5, g_appInfoPtr->m_fRate*5, width()-g_appInfoPtr->m_fRate*10, height()-g_appInfoPtr->m_fRate*10);
+
+    ui->lw_objectiveScore->setGeometry(g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*10,
+                                       ui->widget_objevtiveScore->width() - g_appInfoPtr->m_fRate*20*2,
+                                       ui->widget_objevtiveScore->height() - g_appInfoPtr->m_fRate*10*2);
+   /*
+	QListWidgetItem *pItem = new QListWidgetItem;
+    QSize size = pItem->sizeHint();
+    size.setHeight(g_appInfoPtr->m_fRate*46);
+    pItem->setSizeHint(size);
+    ui->lw_objectiveScore->addItem(pItem);
+    QWidget *w = new QWidget;
+    w->setStyleSheet(QString("QWidget{background:rgba(19, 187, 138, 1); border-radius:%1px}").arg((int)(g_appInfoPtr->m_fRate*10)));
+    QHBoxLayout *l = new QHBoxLayout;
+    QString sLabelStyle = QString(R"(QLabel{
+                                  font-size:%1px;
+                                  font-family:"Microsoft YaHei";
+                                  font-weight:400;
+                                  color:rgba(255,255,255,1);})").arg((int)(g_appInfoPtr->m_fRate*12));
+    QLabel *label_times = new QLabel;
+    label_times->setText(QString::fromLocal8Bit("次数"));
+    label_times->setStyleSheet(sLabelStyle);
+    label_times->adjustSize();
+    QLabel *label_startTime = new QLabel;
+    label_startTime->setText(QString::fromLocal8Bit("开始时间"));
+    label_startTime->setStyleSheet(sLabelStyle);
+    label_startTime->adjustSize();
+    QLabel *label_handinPaperTime = new QLabel;
+    label_handinPaperTime->setText(QString::fromLocal8Bit("交卷时间"));
+    label_handinPaperTime->setStyleSheet(sLabelStyle);
+    label_handinPaperTime->adjustSize();
+    QLabel *label_score = new QLabel;
+    label_score->setText(QString::fromLocal8Bit("客观分"));
+    label_score->setStyleSheet(sLabelStyle);
+    label_score->adjustSize();
+    l->addSpacing(g_appInfoPtr->m_fRate*20);
+    l->addWidget(label_times);
+    l->addStretch();
+    l->addWidget(label_startTime);
+    l->addStretch();
+    l->addWidget(label_handinPaperTime);
+    l->addStretch();
+    l->addWidget(label_score);
+    l->addSpacing(g_appInfoPtr->m_fRate*20);
+
+    w->setLayout(l);
+    ui->lw_objectiveScore->setItemWidget(pItem, w);
+
+    */
+
+    QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect;
+    effect->setOffset(g_appInfoPtr->m_fRate*4,g_appInfoPtr->m_fRate*4);
+    effect->setColor(QColor(0,0,0,30));
+    effect->setBlurRadius(g_appInfoPtr->m_fRate*16);
+    ui->widget_objevtiveScore->setGraphicsEffect(effect);
+}
+
+void clObjectiveScore::onQueryObjectiveScoreList(CQueryObjectiveScoreList queryObjectiveScoreList)
+{
+    if (queryObjectiveScoreList.nCode == 200)
+    {
+        if(m_sExamStudentId == queryObjectiveScoreList.sExamStudentId)
+        {
+            ui->lw_objectiveScore->clear();
+            QListWidgetItem *pItem = new QListWidgetItem;
+            QSize size = pItem->sizeHint();
+            size.setHeight(g_appInfoPtr->m_fRate * 46);
+            pItem->setSizeHint(size);
+            ui->lw_objectiveScore->addItem(pItem);
+            QWidget *w = new QWidget;
+            w->setStyleSheet(QString("QWidget{background:rgba(19, 187, 138, 1); border-radius:%1px}").arg((int)(g_appInfoPtr->m_fRate * 10)));
+            QHBoxLayout *l = new QHBoxLayout;
+            QString sLabelStyle = QString(R"(QLabel{
+                                      font-size:%1px;
+                                      font-family:"Microsoft YaHei";
+                                      font-weight:400;
+                                      color:rgba(255,255,255,1);})").arg((int)(g_appInfoPtr->m_fRate * 12));
+            QLabel *label_times = new QLabel;
+            label_times->setText(QString::fromLocal8Bit("次数"));
+            label_times->setStyleSheet(sLabelStyle);
+            label_times->adjustSize();
+            QLabel *label_startTime = new QLabel;
+            label_startTime->setText(QString::fromLocal8Bit("开始时间"));
+            label_startTime->setStyleSheet(sLabelStyle);
+            label_startTime->adjustSize();
+            QLabel *label_handinPaperTime = new QLabel;
+            label_handinPaperTime->setText(QString::fromLocal8Bit("交卷时间"));
+            label_handinPaperTime->setStyleSheet(sLabelStyle);
+            label_handinPaperTime->adjustSize();
+            QLabel *label_score = new QLabel;
+            label_score->setText(QString::fromLocal8Bit("客观分"));
+            label_score->setStyleSheet(sLabelStyle);
+            label_score->adjustSize();
+            l->addSpacing(g_appInfoPtr->m_fRate * 20);
+            l->addWidget(label_times);
+            l->addStretch();
+            l->addWidget(label_startTime);
+            l->addStretch();
+            l->addWidget(label_handinPaperTime);
+            l->addStretch();
+            l->addWidget(label_score);
+            l->addSpacing(g_appInfoPtr->m_fRate * 20);
+
+            w->setLayout(l);
+            ui->lw_objectiveScore->setItemWidget(pItem, w);
+
+            int nSzie = queryObjectiveScoreList.vObjectiveScore.size();
+            for (int i = nSzie - 1; i >= 0; --i)
+            {
+                CObjectiveScore os = queryObjectiveScoreList.vObjectiveScore[i];
+                QListWidgetItem *pItem = new QListWidgetItem;
+                QSize size = pItem->sizeHint();
+                size.setHeight(g_appInfoPtr->m_fRate * 46);
+                pItem->setSizeHint(size);
+                ui->lw_objectiveScore->addItem(pItem);
+                QWidget *w = new QWidget;
+                w->setStyleSheet(QString("QWidget{background:rgba(255, 255, 255, 1); border-radius:%1px}").arg((int)(g_appInfoPtr->m_fRate * 10)));
+                QHBoxLayout *l = new QHBoxLayout;
+                QString sLabelStyle = QString(R"(QLabel{
+                                          font-size:%1px;
+                                          font-family:"Microsoft YaHei";
+                                          font-weight:400;
+                                          color:rgba(51,51,51,1);})").arg((int)(g_appInfoPtr->m_fRate * 12));
+                QString sLabelScoreStyle = QString(R"(QLabel{
+                                          font-size:%1px;
+                                          font-family:"Microsoft YaHei";
+                                          font-weight:500;
+                                          color:rgba(51,51,51,1);})").arg((int)(g_appInfoPtr->m_fRate * 12));
+
+                QLabel *label_times = new QLabel;
+                label_times->setText(QString::number(os.nExamOrder));
+                label_times->setStyleSheet(sLabelStyle);
+                label_times->adjustSize();
+                QLabel *label_startTime = new QLabel;
+                label_startTime->setText(os.sStartTime);
+                label_startTime->setStyleSheet(sLabelStyle);
+                label_startTime->adjustSize();
+                QLabel *label_handinPaperTime = new QLabel;
+                label_handinPaperTime->setText(os.sEndTime);
+                label_handinPaperTime->setStyleSheet(sLabelStyle);
+                label_handinPaperTime->adjustSize();
+                if (label_handinPaperTime->width() < label_startTime->width())
+                {
+                    label_handinPaperTime->setFixedWidth(label_startTime->width());
+                }
+                QLabel *label_score = new QLabel;
+                QString sScoreText = QString::number(os.fObjectiveScore);
+                if (os.bIsAuditing)
+                {
+                    sScoreText = QString::fromLocal8Bit("审核中");
+                }
+                if (os.bIsIllegality)
+                {
+                    sScoreText = QString::fromLocal8Bit("违纪");
+                }
+                label_score->setText(sScoreText);
+                label_score->setStyleSheet(sLabelScoreStyle);
+                label_score->adjustSize();
+                l->addSpacing(g_appInfoPtr->m_fRate * 20);
+                l->addWidget(label_times);
+                l->addStretch();
+                l->addWidget(label_startTime);
+                l->addStretch();
+                l->addWidget(label_handinPaperTime);
+                l->addStretch();
+                l->addWidget(label_score);
+                l->addSpacing(g_appInfoPtr->m_fRate * 20);
+
+                w->setLayout(l);
+                ui->lw_objectiveScore->setItemWidget(pItem, w);
+            }
+            int nHeight = g_appInfoPtr->m_fRate * 46*(nSzie+1) + g_appInfoPtr->m_fRate * (10 + 5) + g_appInfoPtr->m_fRate * (20 + 5);
+            emit objectiveScoreHeight(m_nRow, nHeight);
+        }
+	}
+	else
+	{
+        if(queryObjectiveScoreList.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取客观题成绩失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(queryObjectiveScoreList.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}

+ 30 - 0
client/clObjectiveScore.h

@@ -0,0 +1,30 @@
+#ifndef CLOBJECTIVESCORE_H
+#define CLOBJECTIVESCORE_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+
+namespace Ui {
+class clObjectiveScore;
+}
+
+class clObjectiveScore : public QWidget
+{
+    Q_OBJECT
+
+signals:
+	void objectiveScoreHeight(int nRow, int nHeight);
+public:
+    explicit clObjectiveScore(int nRow, QString sExamStudentId, QWidget *parent = nullptr);
+    ~clObjectiveScore();
+	int getRow() { return m_nRow; }
+    void setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight);
+private slots:
+	void onQueryObjectiveScoreList(CQueryObjectiveScoreList queryObjectiveScoreList);
+private:
+    Ui::clObjectiveScore *ui;
+	QString m_sExamStudentId;
+	int m_nRow;
+};
+
+#endif // CLOBJECTIVESCORE_H

+ 39 - 0
client/clObjectiveScore.ui

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clObjectiveScore</class>
+ <widget class="QWidget" name="clObjectiveScore">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>503</width>
+    <height>415</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_objevtiveScore" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>501</width>
+     <height>411</height>
+    </rect>
+   </property>
+   <widget class="QListWidget" name="lw_objectiveScore">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>10</y>
+      <width>481</width>
+      <height>391</height>
+     </rect>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 481 - 0
client/clOfflineExam.cpp

@@ -0,0 +1,481 @@
+#include "clOfflineExam.h"
+#include "ui_clOfflineExam.h"
+
+#include "CAppInfo.h"
+#include "clOperation.h"
+#include "awMsgBox.h"
+#include <QScrollBar>
+#include <QFileDialog>
+
+
+clOfflineExam::clOfflineExam(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clOfflineExam)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	qRegisterMetaType<CGetOfflineCourse>("CGetOfflineCourse");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetOfflineCourse, this, &clOfflineExam::onGetOfflineCourse);
+	qRegisterMetaType<CGetOffLineExamAnswerSheet>("CGetOffLineExamAnswerSheet");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetOffLineExamAnswerSheet, this, &clOfflineExam::onGetOffLineExamAnswerSheet);
+	qRegisterMetaType<CStartOfflineExam>("CStartOfflineExam");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnStartOfflineExam, this, &clOfflineExam::onStartOfflineExam);
+	qRegisterMetaType<CGetOffLineExamPaper>("CGetOffLineExamPaper");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetOffLineExamPaper, this, &clOfflineExam::onGetOffLineExamPaper);
+	qRegisterMetaType<CPreviewOffLineExamPaper>("CPreviewOffLineExamPaper");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnPreviewOffLineExamPaper, this, &clOfflineExam::onPreviewOffLineExamPaper);	
+	qRegisterMetaType<CGetExamProperty>("CGetExamProperty");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetExamProperty, this, &clOfflineExam::onGetExamProperty);
+    qRegisterMetaType<CDownLoadFileInfo>("CDownLoadFileInfo");
+    connect(g_httpBllPtr.get(), &CHttpBll::sgnDownLoadFile, this, &clOfflineExam::onDownLoadFile);
+}
+
+clOfflineExam::~clOfflineExam()
+{
+    delete ui;
+}
+
+void clOfflineExam::setUI(const int nWidth, const int nHeight)
+{
+    setGeometry(0, 0, nWidth, nHeight);
+    ui->widget_offlineExam->setGeometry(0, 0, width(), height());
+    ui->widget_op_top->setGeometry(0, 0, width(), g_appInfoPtr->m_fRate*72);
+    ui->btn_downloadAnswerSheet->setGeometry(ui->widget_op_top->width() - g_appInfoPtr->m_fRate*30 - g_appInfoPtr->m_fRate*144,
+                                             (ui->widget_op_top->height() - g_appInfoPtr->m_fRate*32)/2,
+                                             g_appInfoPtr->m_fRate*144, g_appInfoPtr->m_fRate*32);
+	ui->btn_downloadAnswerSheet->setVisible(false);
+	ui->btn_downloadAnswerSheet->setIconSize(QSize(g_appInfoPtr->m_fRate*14, g_appInfoPtr->m_fRate*14));
+    ui->btn_downloadAnswerSheet->setIcon(QIcon(":/images/icon-download-answer-sheet.png"));
+    ui->tablewt_examList->setGeometry(g_appInfoPtr->m_fRate*30, ui->widget_op_top->height() + g_appInfoPtr->m_fRate*10,
+                                      ui->widget_offlineExam->width() - g_appInfoPtr->m_fRate*30*2,
+                                      ui->widget_offlineExam->height() - ui->widget_op_top->height() - g_appInfoPtr->m_fRate*(10 + 62));
+    ui->tablewt_examList->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablewt_examList->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablewt_examList->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablewt_examList->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablewt_examList->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablewt_examList->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablewt_examList->setRowCount(10);
+    //设置列数
+    ui->tablewt_examList->setColumnCount(5);
+
+    ui->tablewt_examList->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablewt_examList->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+    QStringList sHeadStr;
+    sHeadStr << QString::fromLocal8Bit("课程") <<
+                QString::fromLocal8Bit("专业") <<
+                QString::fromLocal8Bit("进入考试时间") <<
+                QString::fromLocal8Bit("状态") <<
+                QString::fromLocal8Bit("操作");
+
+    ui->tablewt_examList->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablewt_examList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablewt_examList->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablewt_examList->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    ui->tablewt_examList->setColumnWidth(0, g_appInfoPtr->m_fRate*240);
+    ui->tablewt_examList->setColumnWidth(1, g_appInfoPtr->m_fRate*220);
+    ui->tablewt_examList->setColumnWidth(2, g_appInfoPtr->m_fRate*340);
+    ui->tablewt_examList->setColumnWidth(3, g_appInfoPtr->m_fRate*90);
+    ui->tablewt_examList->setColumnWidth(4, g_appInfoPtr->m_fRate*232);
+
+    ui->tablewt_examList->horizontalHeader()->setStretchLastSection(true);
+
+    
+}
+
+void clOfflineExam::showEvent(QShowEvent *)
+{
+	CHttpRequestPackage hrp1;
+	hrp1.sUri = "/api/ecs_core/org/getAnswersUrl/" + g_appInfoPtr->m_sRootOrgId;
+	hrp1.nRequestType = RequestType::rtGetOffLineExamAnswerSheet;
+	g_httpBllPtr->get(hrp1);
+
+	CHttpRequestPackage hrp;
+	hrp.sUri = "/api/ecs_oe_admin/client/exam/process/getOfflineCourse";
+	hrp.nRequestType = RequestType::rtGetOfflineCourse;
+	g_httpBllPtr->post(hrp);	
+}
+
+void clOfflineExam::onGetOfflineCourse(CGetOfflineCourse getOfflineCourse)
+{
+	if (getOfflineCourse.nCode == 200)
+	{
+		int nSize = getOfflineCourse.vOfflineCourseInfo.size();
+		for (int i = 0; i < nSize; ++i)
+		{
+			COfflineCourseInfo oci = getOfflineCourse.vOfflineCourseInfo[i];
+			ui->tablewt_examList->setItem(i, 0, new QTableWidgetItem(oci.sCourseName));
+			ui->tablewt_examList->setItem(i, 1, new QTableWidgetItem(oci.sSpecialtyName));
+			ui->tablewt_examList->setItem(i, 2, new QTableWidgetItem(QString::fromLocal8Bit("%1 ~ %2").arg(oci.sStartTime).arg(oci.sEndTime)));
+            CL_OPERATION_TYPE sAnswerStatus = CL_OPERATION_TYPE::cot_offline_status_null;
+			CL_OPERATION_TYPE otStatus = CL_OPERATION_TYPE::cot_offline_init_exam;
+			if (!oci.sStatus.isEmpty())
+			{
+				otStatus = CL_OPERATION_TYPE::cot_offline_exam;
+				if (oci.vOfflineFileInfo.size() > 0)
+				{
+                    sAnswerStatus = CL_OPERATION_TYPE::cot_offline_status_download;
+				}				
+			}
+            clOperation *cloStatus = new clOperation(i, ui->tablewt_examList);
+            connect(cloStatus, &clOperation::downloadOfflineAnswer, this, [&](int nRow){
+                QString sExportDir = QFileDialog::getExistingDirectory(
+                    this, QString::fromLocal8Bit("选择导出目录"),
+                    "/");
+
+                if (sExportDir.isEmpty())
+                {
+                    return;
+                }
+
+                //导出
+                if (sExportDir.right(1) != "/")
+                {
+                    sExportDir += "/";
+                }
+
+                COfflineCourseInfo oci = m_vOfflineCourseInfo[nRow];
+                for (COfflineFileInfo &fileInfo : oci.vOfflineFileInfo)
+                {
+                    QString sFileName = fileInfo.sOfflineFileUrl.right(fileInfo.sOfflineFileUrl.length() - fileInfo.sOfflineFileUrl.lastIndexOf("/") - 1);
+                    QString sAnswerFile = sExportDir + sFileName;
+                    fileInfo.sLocalFileName = sAnswerFile;
+                    CHttpRequestPackage hrp;
+                    hrp.sUri = fileInfo.sOfflineFileUrl;
+                    hrp.sCommonStr = QString("%1,%2").arg(sAnswerFile).arg(QString::number(nRow));
+                    hrp.sCommonStr1 = __FILE__;
+                    hrp.nRequestType = RequestType::rtDownLoadFile;
+
+                    g_httpBllPtr->downLoad(hrp);
+                }
+            });
+
+            cloStatus->setUI(ui->tablewt_examList->columnWidth(4), ui->tablewt_examList->rowHeight(i), sAnswerStatus);
+            ui->tablewt_examList->setCellWidget(i, 3, cloStatus);
+
+
+			clOperation *clo = new clOperation(i, ui->tablewt_examList);
+            connect(clo, &clOperation::offlineExamStart, this, [&](int nRow){
+                COfflineCourseInfo oci = m_vOfflineCourseInfo[nRow];
+				
+				CHttpRequestPackage hrp;
+				hrp.sUri = "/api/ecs_oe_admin/client/exam/process/startOfflineExam";
+				hrp.sParamList.push_back(QString("examStudentId,%1").arg(oci.nExamStudentId));
+				hrp.nRequestType = RequestType::rtStartOfflineExam;
+				hrp.eParamType = HttpParamType::hptUrl;
+				hrp.sCommonStr = QString::number(nRow);
+				g_httpBllPtr->post(hrp);
+            });
+
+            connect(clo, &clOperation::viewOfflinePaper, this, [&](int nRow){
+                COfflineCourseInfo oci = m_vOfflineCourseInfo[nRow];
+				
+				CHttpRequestPackage hrp;
+				hrp.sUri = QString("/api/branch_ecs_ques/paper/%1").arg(oci.sPaperId);				
+				hrp.nRequestType = RequestType::rtPreviewOffLineExamPaper;				
+				hrp.sCommonStr = QString::number(nRow);
+				g_httpBllPtr->get(hrp);
+            });
+
+            connect(clo, &clOperation::downloadOfflinePaper, this, [&](int nRow){
+                COfflineCourseInfo oci = m_vOfflineCourseInfo[nRow];
+								
+				QString sExportDir = QFileDialog::getExistingDirectory(
+                    this, QString::fromLocal8Bit("选择下载目录"),
+					"/");
+
+				if (sExportDir.isEmpty())
+				{
+					return;
+				}
+
+				//导出
+				if (sExportDir.right(1) != "/")
+				{
+					sExportDir += "/";
+				}
+				
+				QString sPaperFile = sExportDir + oci.sCourseCode + "_" + oci.sCourseName + ".zip";
+				if (!QFile::exists(sPaperFile))
+				{
+					CHttpRequestPackage hrp;
+					hrp.sUri = QString("/api/branch_ecs_ques/paper/export/%1/PAPER/offLine").arg(oci.sPaperId);
+					hrp.nRequestType = RequestType::rtGetOffLineExamPaper;
+					hrp.sCommonStr = sPaperFile;
+					g_httpBllPtr->get(hrp);						
+				}
+				
+            });
+
+            connect(clo, &clOperation::uploadOfflineAnswer, this, [&](int nRow){
+                COfflineCourseInfo oci = m_vOfflineCourseInfo[nRow];
+
+				CHttpRequestPackage hrp;
+				hrp.sUri = QString("/api/ecs_exam_work/exam/getExamPropertyFromCacheByStudentSession/%1/%2")
+					.arg(oci.nExamId).arg("OFFLINE_UPLOAD_FILE_TYPE");
+				hrp.nRequestType = RequestType::rtGetExamProperty;
+				hrp.sCommonStr = "OFFLINE_UPLOAD_FILE_TYPE;" + QString::number(oci.nExamRecordDataId);
+				g_httpBllPtr->get(hrp);								
+            });
+
+			clo->setUI(ui->tablewt_examList->columnWidth(4), ui->tablewt_examList->rowHeight(i), otStatus);
+			ui->tablewt_examList->setCellWidget(i, 4, clo);
+		}
+        m_vOfflineCourseInfo.swap(getOfflineCourse.vOfflineCourseInfo);
+	}
+	else
+	{
+        if(getOfflineCourse.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取离线考试信息失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(getOfflineCourse.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clOfflineExam::onDownLoadFile(CDownLoadFileInfo downLoadFileInfo)
+{
+    if (downLoadFileInfo.sModuleName == __FILE__)
+    {
+        QStringList list = downLoadFileInfo.sFileName.split(",");
+        if(list.count() == 2)
+        {
+            if(downLoadFileInfo.nCode == 200)
+            {
+                if(list[1] == "downloadAnswerSheet")
+                {
+                    ShowMsg(QString::fromLocal8Bit("下载完成"), (QWidget*)(this->parent()));
+                }
+                else
+                {
+                    int nRow = list[1].toInt();
+                    COfflineCourseInfo oci = m_vOfflineCourseInfo[nRow];
+                    QString sLocalFileName = oci.vOfflineFileInfo[oci.vOfflineFileInfo.size() - 1].sLocalFileName;
+
+                    if(sLocalFileName == list[0])
+                    {
+                        ShowMsg(QString::fromLocal8Bit("下载完成"), (QWidget*)(this->parent()));
+                    }
+                }
+            }
+            else
+            {
+                if(downLoadFileInfo.sMessage.isEmpty())
+                {
+                    ShowMsg(QString::fromLocal8Bit("下载失败"), g_appInfoPtr->m_pAnsBgWidget);
+                }
+                else
+                {
+                    ShowMsg(downLoadFileInfo.sMessage, this);
+                }
+            }
+        }
+    }
+
+//    downLoadFileInfo
+//    QString sFileName = g_appInfoPtr->m_sStudentPhotoPath.right(g_appInfoPtr->m_sStudentPhotoPath.length() - g_appInfoPtr->m_sStudentPhotoPath.lastIndexOf("/") - 1);
+//    QString sAudioFile = g_appInfoPtr->m_sCacheFileDir + "cache/" + sFileName;
+//    if (downLoadFileInfo.sFileName == sAudioFile)
+//    {
+//        if (downLoadFileInfo.nCode == 200)
+//        {
+//            int nSecord = g_audioPalyerPtr->GetMediaDuration(sAudioFile);
+//            m_nMaxTestSecord = nSecord;
+//            QString sText = QString("00:00 / %1:%2")
+//                    .arg((int)(nSecord/60), 2, 10, QChar('0'))
+//                    .arg((int)(nSecord%60), 2, 10, QChar('0'));
+//            ui->label_ap_time->setText(sText);
+//        }
+//        else
+//        {
+
+//        }
+//    }
+}
+
+void clOfflineExam::onGetOffLineExamAnswerSheet(CGetOffLineExamAnswerSheet getOffLineExamAnswerSheet)
+{
+	if (getOffLineExamAnswerSheet.nCode == 200)
+	{
+		if (!getOffLineExamAnswerSheet.sAnswerSheetUrl.isEmpty())
+		{
+            m_sAnswerSheetUrl = getOffLineExamAnswerSheet.sAnswerSheetUrl;
+			ui->btn_downloadAnswerSheet->setVisible(true);
+		}
+	}
+	else
+	{
+        if(getOffLineExamAnswerSheet.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取离线考试信息失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(getOffLineExamAnswerSheet.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clOfflineExam::onStartOfflineExam(CStartOfflineExam startOfflineExam)
+{
+	if (startOfflineExam.nCode == 200)
+	{
+		int nRow = startOfflineExam.nRow;
+		COfflineCourseInfo &oci = m_vOfflineCourseInfo[nRow];
+		oci.sStatus = "EXAM_ING";
+		
+		clOperation *op = dynamic_cast<clOperation*>(ui->tablewt_examList->cellWidget(nRow, 4));
+		if (op)
+		{
+			op->setOperationType(CL_OPERATION_TYPE::cot_offline_exam);
+		}
+
+		CHttpRequestPackage hrp;
+		hrp.sUri = QString("/api/branch_ecs_ques/paper/export/%1/PAPER/offLine").arg(oci.sPaperId);
+		hrp.nRequestType = RequestType::rtGetOffLineExamPaper;
+		hrp.sCommonStr = QString::number(nRow);
+		g_httpBllPtr->get(hrp);
+	}
+	else
+	{
+        if(startOfflineExam.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("离线考试开考失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(startOfflineExam.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clOfflineExam::onGetOffLineExamPaper(CGetOffLineExamPaper getOffLineExamPaper)
+{
+	if (getOffLineExamPaper.nCode == 200)
+	{
+		ShowMsg(QString::fromLocal8Bit("下载试卷完成"), this);
+	}
+	else
+	{
+        if(getOffLineExamPaper.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("离线考试下载试卷失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(getOffLineExamPaper.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clOfflineExam::onPreviewOffLineExamPaper(CPreviewOffLineExamPaper previewOffLineExamPaper)
+{
+	if (previewOffLineExamPaper.nCode == 200)
+	{
+		if (m_pViewPaper == nullptr)
+		{
+			m_pViewPaper = std::make_shared<cloeViewPaper>(previewOffLineExamPaper.sUrl, (QWidget*)this->parent()->parent());
+            connect(m_pViewPaper.get(), &cloeViewPaper::goback, this, [&](){
+               m_pViewPaper.reset();
+            });
+		}
+		m_pViewPaper->show();
+	}
+	else
+	{
+        if(previewOffLineExamPaper.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("离线考试下载试卷失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(previewOffLineExamPaper.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clOfflineExam::onGetExamProperty(CGetExamProperty getExamProperty)
+{
+	if (getExamProperty.nCode == 200)
+	{
+		QStringList sList = getExamProperty.sType.split(";");
+		if (sList.count() != 2)
+		{
+			return;
+		}
+		if (sList[0] == "OFFLINE_UPLOAD_FILE_TYPE")
+		{
+            if(m_pUploadFile == nullptr)
+            {
+                m_pUploadFile = std::make_shared<cloeUploadFile>(sList[1].toLongLong(), getExamProperty.vOfflineUploadFileType, (QWidget*)this->parent()->parent());
+                connect(m_pUploadFile.get(), &cloeUploadFile::cancel, this, [&](){
+                    m_pUploadFile.reset();
+                });
+
+				connect(m_pUploadFile.get(), &cloeUploadFile::uploadSucceed, this, [&]() {
+					m_pUploadFile.reset();
+				
+					CHttpRequestPackage hrp;
+					hrp.sUri = "/api/ecs_oe_admin/client/exam/process/getOfflineCourse";
+					hrp.nRequestType = RequestType::rtGetOfflineCourse;
+					g_httpBllPtr->post(hrp);
+				});
+            }
+            m_pUploadFile->show();
+		}
+	}
+    else
+    {
+        if(getExamProperty.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取离线考试信息失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(getExamProperty.sMessage, (QWidget*)(this->parent()));
+        }
+    }
+}
+
+
+void clOfflineExam::on_btn_downloadAnswerSheet_clicked()
+{
+    QString sExportDir = QFileDialog::getExistingDirectory(
+        this, QString::fromLocal8Bit("选择下载目录"),
+        "/");
+
+    if (sExportDir.isEmpty())
+    {
+        return;
+    }
+
+    //导出
+    if (sExportDir.right(1) != "/")
+    {
+        sExportDir += "/";
+    }
+
+    QString sFileName = m_sAnswerSheetUrl.right(m_sAnswerSheetUrl.length() - m_sAnswerSheetUrl.lastIndexOf("/") - 1);
+    QString sAnswerFile = sExportDir + sFileName;
+
+    CHttpRequestPackage hrp;
+    hrp.sUri = m_sAnswerSheetUrl;
+    hrp.sCommonStr = QString("%1,%2").arg(sAnswerFile).arg("downloadAnswerSheet");
+    hrp.sCommonStr1 = __FILE__;
+    hrp.nRequestType = RequestType::rtDownLoadFile;
+
+    g_httpBllPtr->downLoad(hrp);
+}

+ 43 - 0
client/clOfflineExam.h

@@ -0,0 +1,43 @@
+#ifndef CLOFFLINEEXAM_H
+#define CLOFFLINEEXAM_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+#include "cloeViewPaper.h"
+#include "cloeUploadFile.h"
+
+
+namespace Ui {
+class clOfflineExam;
+}
+
+class clOfflineExam : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit clOfflineExam(QWidget *parent = nullptr);
+    ~clOfflineExam();
+
+    void setUI(const int nWidth, const int nHeight);
+private slots:
+	void onGetOfflineCourse(CGetOfflineCourse getOfflineCourse);
+	void onGetOffLineExamAnswerSheet(CGetOffLineExamAnswerSheet getOffLineExamAnswerSheet);
+	void onStartOfflineExam(CStartOfflineExam startOfflineExam);
+	void onGetOffLineExamPaper(CGetOffLineExamPaper getOffLineExamPaper);
+	void onPreviewOffLineExamPaper(CPreviewOffLineExamPaper previewOffLineExamPaper);
+	void onDownLoadFile(CDownLoadFileInfo downLoadFileInfo);
+	void onGetExamProperty(CGetExamProperty getExamProperty);
+    void on_btn_downloadAnswerSheet_clicked();
+
+private:
+	void showEvent(QShowEvent *);
+
+    Ui::clOfflineExam *ui;
+    std::vector<COfflineCourseInfo> m_vOfflineCourseInfo;
+	std::shared_ptr<cloeViewPaper> m_pViewPaper;
+    std::shared_ptr<cloeUploadFile> m_pUploadFile;
+    QString m_sAnswerSheetUrl;
+};
+
+#endif // CLOFFLINEEXAM_H

+ 62 - 0
client/clOfflineExam.ui

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clOfflineExam</class>
+ <widget class="QWidget" name="clOfflineExam">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>784</width>
+    <height>526</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_offlineExam" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>871</width>
+     <height>571</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_op_top" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>931</width>
+      <height>80</height>
+     </rect>
+    </property>
+    <widget class="QPushButton" name="btn_downloadAnswerSheet">
+     <property name="geometry">
+      <rect>
+       <x>604</x>
+       <y>30</y>
+       <width>161</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>下载答题纸模板</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QTableWidget" name="tablewt_examList">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>80</y>
+      <width>871</width>
+      <height>401</height>
+     </rect>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 286 - 0
client/clOnlineExam.cpp

@@ -0,0 +1,286 @@
+#include "clOnlineExam.h"
+#include "ui_clOnlineExam.h"
+
+#include "CAppInfo.h"
+#include "logproc.h"
+#include <QScrollBar>
+#include "awMsgBox.h"
+
+clOnlineExam::clOnlineExam(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clOnlineExam)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	qRegisterMetaType<CQueryExamList>("CQueryExamList");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnQueryExamList, this, &clOnlineExam::onQueryExamList);
+	qRegisterMetaType<CQueryExamEndList>("CQueryExamEndList");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnQueryExamEndList, this, &clOnlineExam::onQueryExamEndList);
+
+	m_pObjectiveScoreTimer = std::make_shared<QTimer>();
+	m_pObjectiveScoreTimer->setInterval(200);
+	connect(m_pObjectiveScoreTimer.get(), &QTimer::timeout, this, [&]() {
+		m_pObjectiveScoreTimer->stop();
+		if (m_pObjectiveScore != nullptr)
+		{
+			m_pObjectiveScore.reset();
+		}
+	});
+}
+
+clOnlineExam::~clOnlineExam()
+{
+    delete ui;
+}
+
+void clOnlineExam::setUI(const int nWidth, const int nHeight)
+{
+    setGeometry(0, 0, nWidth, nHeight);
+    ui->tabw_onlineExam->setGeometry(0, 0, nWidth, nHeight);
+    ui->tabw_onlineExam->setTabText(0, QString::fromLocal8Bit("待考列表"));
+    ui->tabw_onlineExam->setTabText(1, QString::fromLocal8Bit("已完成考试"));
+    ui->vlayout_el->setContentsMargins(g_appInfoPtr->m_fRate*30, 0, g_appInfoPtr->m_fRate*30, 0);
+    ui->vlayout_fel->setContentsMargins(g_appInfoPtr->m_fRate*30, 0, g_appInfoPtr->m_fRate*30, 0);
+
+    ui->tablewt_examList->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablewt_examList->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablewt_examList->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablewt_examList->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablewt_examList->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablewt_examList->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablewt_examList->setRowCount(10);
+    //设置列数
+    ui->tablewt_examList->setColumnCount(7);
+
+    ui->tablewt_examList->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablewt_examList->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+    QStringList sHeadStr;
+    sHeadStr << QString::fromLocal8Bit("课程") <<
+                QString::fromLocal8Bit("层次") <<
+                QString::fromLocal8Bit("专业") <<
+                QString::fromLocal8Bit("考试进入时间") <<
+                QString::fromLocal8Bit("考试时间周期") <<
+                QString::fromLocal8Bit("剩余考试次数") <<
+                QString::fromLocal8Bit("操作");
+
+    ui->tablewt_examList->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablewt_examList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablewt_examList->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablewt_examList->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    ui->tablewt_examList->setColumnWidth(0, g_appInfoPtr->m_fRate*116);
+    ui->tablewt_examList->setColumnWidth(1, g_appInfoPtr->m_fRate*56);
+    ui->tablewt_examList->setColumnWidth(2, g_appInfoPtr->m_fRate*104);
+    ui->tablewt_examList->setColumnWidth(3, g_appInfoPtr->m_fRate*277);
+    ui->tablewt_examList->setColumnWidth(4, g_appInfoPtr->m_fRate*305);
+    ui->tablewt_examList->setColumnWidth(5, g_appInfoPtr->m_fRate*102);
+    ui->tablewt_examList->setColumnWidth(6, g_appInfoPtr->m_fRate*146);
+    ui->tablewt_examList->horizontalHeader()->setStretchLastSection(true);
+
+    ui->tablew_finishExamList->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablew_finishExamList->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablew_finishExamList->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablew_finishExamList->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablew_finishExamList->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablew_finishExamList->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablew_finishExamList->setRowCount(10);
+    //设置列数
+    ui->tablew_finishExamList->setColumnCount(7);
+
+    ui->tablew_finishExamList->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablew_finishExamList->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+
+
+    ui->tablew_finishExamList->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablew_finishExamList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablew_finishExamList->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablew_finishExamList->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    ui->tablew_finishExamList->setColumnWidth(0, g_appInfoPtr->m_fRate*116);
+    ui->tablew_finishExamList->setColumnWidth(1, g_appInfoPtr->m_fRate*56);
+    ui->tablew_finishExamList->setColumnWidth(2, g_appInfoPtr->m_fRate*104);
+    ui->tablew_finishExamList->setColumnWidth(3, g_appInfoPtr->m_fRate*277);
+    ui->tablew_finishExamList->setColumnWidth(4, g_appInfoPtr->m_fRate*305);
+    ui->tablew_finishExamList->setColumnWidth(5, g_appInfoPtr->m_fRate*102);
+    ui->tablew_finishExamList->setColumnWidth(6, g_appInfoPtr->m_fRate*146);
+    ui->tablew_finishExamList->horizontalHeader()->setStretchLastSection(true);
+
+}
+
+void clOnlineExam::showEvent(QShowEvent *)
+{
+	CHttpRequestPackage hrp;
+	hrp.sUri = "/api/ecs_oe_admin/client/exam/process/queryExamList";
+	hrp.nRequestType = RequestType::rtQueryExamList;
+	g_httpBllPtr->post(hrp);
+
+	CHttpRequestPackage hrp1;
+	hrp1.sUri = "/api/ecs_oe_admin/client/exam/process/queryExamEndList";
+	hrp1.nRequestType = RequestType::rtQueryExamEndList;
+	g_httpBllPtr->post(hrp1);
+}
+
+void clOnlineExam::onQueryExamList(CQueryExamList queryExamList)
+{
+	if (queryExamList.nCode == 200)
+	{
+		qDebug() << "onQueryExamList";
+		int nSize = queryExamList.vOnlieExamList.size();
+		for (int i = 0; i < nSize; ++i)
+		{
+			CExamCourseInfo eci = queryExamList.vOnlieExamList[i];
+			ui->tablewt_examList->setItem(i, 0, new QTableWidgetItem(eci.sCourseName));
+			ui->tablewt_examList->setItem(i, 1, new QTableWidgetItem(eci.sCourseLevel));
+			ui->tablewt_examList->setItem(i, 2, new QTableWidgetItem(eci.sSpecialtyName));
+			ui->tablewt_examList->setItem(i, 3, new QTableWidgetItem(QString::fromLocal8Bit("%1 ~ %2").arg(eci.sStartTime).arg(eci.sEndTime)));
+			ui->tablewt_examList->setItem(i, 4, new QTableWidgetItem(QString::fromLocal8Bit("")));
+			ui->tablewt_examList->setItem(i, 5, new QTableWidgetItem(QString::number(eci.nAllowExamCount)));
+			clOperation *clo = new clOperation(i, ui->tablewt_examList);
+			clo->setUI(ui->tablewt_examList->columnWidth(6), ui->tablewt_examList->rowHeight(i), CL_OPERATION_TYPE::cot_online_exam);
+			ui->tablewt_examList->setCellWidget(i, 6, clo);
+            connect(clo, &clOperation::enterExam, this, [&](CL_OPERATION_TYPE cot, int nRow){
+                if (cot == CL_OPERATION_TYPE::cot_online_exam )
+                {
+                    emit enterExam(cot, m_vOnlieExamList[nRow].nExamId, m_vOnlieExamList[nRow].nExamStudentId);
+                }
+            });
+			connect(clo, &clOperation::showObjectiveScore, this, [&](CL_OPERATION_TYPE cot, int nRow) {
+				if (cot == CL_OPERATION_TYPE::cot_online_exam )
+				{					
+					m_pObjectiveScoreTimer->stop();
+					
+					if (m_pObjectiveScore != nullptr &&
+						m_pObjectiveScore->getRow() != nRow)
+					{
+						m_pObjectiveScore.reset();
+					}
+					
+					if (m_pObjectiveScore == nullptr)
+					{
+						m_pObjectiveScore = std::make_shared<clObjectiveScore>(nRow, QString::number(m_vOnlieExamList[nRow].nExamStudentId), this);
+						connect(m_pObjectiveScore.get(), &clObjectiveScore::objectiveScoreHeight, this, [&](int nRow, int nHeight) {
+							int ntableTop =  ui->tabw_onlineExam->y() + g_appInfoPtr->m_fRate*70;
+							int nTop = ntableTop + (nRow + 2)* ui->tablewt_examList->verticalHeader()->defaultSectionSize() - nHeight / 2;
+							if (nTop < ui->tabw_onlineExam->y())
+							{
+								nTop = ui->tabw_onlineExam->y();
+							}
+							else if ((nTop + nHeight) > (ntableTop + ui->tablewt_examList->height()))
+							{
+								nTop = ntableTop + ui->tablewt_examList->height() - nHeight;
+							}
+							m_pObjectiveScore->setUI(ui->tablewt_examList->x() + ui->tablewt_examList->width() - ui->tablewt_examList->columnWidth(6) - g_appInfoPtr->m_fRate * 530,
+								nTop, g_appInfoPtr->m_fRate * 530, nHeight);
+							m_pObjectiveScore->show();
+						});
+						
+					}
+				}
+			    
+			});
+
+			connect(clo, &clOperation::hideObjectiveScore, this, [&](CL_OPERATION_TYPE cot, int nRow) {
+				if (cot == CL_OPERATION_TYPE::cot_online_exam)
+				{
+					m_pObjectiveScoreTimer->start();	
+				}
+			});
+		}
+		m_vOnlieExamList.swap(queryExamList.vOnlieExamList);
+	}
+	else
+	{
+        if(queryExamList.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取离线考试信息失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(queryExamList.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clOnlineExam::onQueryExamEndList(CQueryExamEndList queryExamEndList)
+{
+	if (queryExamEndList.nCode == 200)
+	{
+		int nSize = queryExamEndList.vOnlieEndExamList.size();
+		for (int i = 0; i < nSize; ++i)
+		{
+			CExamCourseInfo eci = queryExamEndList.vOnlieEndExamList[i];
+			ui->tablew_finishExamList->setItem(i, 0, new QTableWidgetItem(eci.sCourseName));
+			ui->tablew_finishExamList->setItem(i, 1, new QTableWidgetItem(eci.sCourseLevel));
+			ui->tablew_finishExamList->setItem(i, 2, new QTableWidgetItem(eci.sSpecialtyName));
+			ui->tablew_finishExamList->setItem(i, 3, new QTableWidgetItem(QString::fromLocal8Bit("%1 ~ %2").arg(eci.sStartTime).arg(eci.sEndTime)));
+			ui->tablew_finishExamList->setItem(i, 4, new QTableWidgetItem(QString::fromLocal8Bit("")));
+			ui->tablew_finishExamList->setItem(i, 5, new QTableWidgetItem(QString::number(eci.nAllowExamCount)));
+			clOperation *clo = new clOperation(i, ui->tablew_finishExamList);
+			clo->setUI(ui->tablew_finishExamList->columnWidth(6), ui->tablew_finishExamList->rowHeight(i), CL_OPERATION_TYPE::cot_online_finish_exam);
+			ui->tablew_finishExamList->setCellWidget(i, 6, clo);
+
+            connect(clo, &clOperation::showObjectiveScore, this, [&](CL_OPERATION_TYPE cot, int nRow) {
+                if (cot == CL_OPERATION_TYPE::cot_online_finish_exam )
+                {
+                    m_pObjectiveScoreTimer->stop();
+
+                    if (m_pObjectiveScore != nullptr &&
+                        m_pObjectiveScore->getRow() != nRow)
+                    {
+                        m_pObjectiveScore.reset();
+                    }
+
+                    if (m_pObjectiveScore == nullptr)
+                    {
+                        m_pObjectiveScore = std::make_shared<clObjectiveScore>(nRow, QString::number(m_vOnlieEndExamList[nRow].nExamStudentId), this);
+                        connect(m_pObjectiveScore.get(), &clObjectiveScore::objectiveScoreHeight, this, [&](int nRow, int nHeight) {
+                            int ntableTop =  ui->tablew_finishExamList->y() + g_appInfoPtr->m_fRate*70;
+                            int nTop = ntableTop + (nRow + 2)* ui->tablew_finishExamList->verticalHeader()->defaultSectionSize() - nHeight / 2;
+                            if (nTop < ui->tablew_finishExamList->y())
+                            {
+                                nTop = ui->tablew_finishExamList->y();
+                            }
+                            else if ((nTop + nHeight) > (ntableTop + ui->tablew_finishExamList->height()))
+                            {
+                                nTop = ntableTop + ui->tablew_finishExamList->height() - nHeight;
+                            }
+                            m_pObjectiveScore->setUI(ui->tablew_finishExamList->x() + ui->tablew_finishExamList->width() - ui->tablew_finishExamList->columnWidth(6) - g_appInfoPtr->m_fRate * 530,
+                                nTop, g_appInfoPtr->m_fRate * 530, nHeight);
+                            m_pObjectiveScore->show();
+                        });
+
+                    }
+                }
+
+            });
+
+            connect(clo, &clOperation::hideObjectiveScore, this, [&](CL_OPERATION_TYPE cot, int nRow) {
+                if (cot == CL_OPERATION_TYPE::cot_online_finish_exam)
+                {
+                    m_pObjectiveScoreTimer->start();
+                }
+            });
+		}
+        m_vOnlieEndExamList.swap(queryExamEndList.vOnlieEndExamList);
+	}
+	else
+	{
+	
+	}
+}

+ 38 - 0
client/clOnlineExam.h

@@ -0,0 +1,38 @@
+#ifndef CLONLINEEXAM_H
+#define CLONLINEEXAM_H
+
+#include <QWidget>
+#include <QTimer>
+#include "clObjectiveScore.h"
+#include "CHttpBll.h"
+#include "clOperation.h"
+
+namespace Ui {
+class clOnlineExam;
+}
+
+class clOnlineExam : public QWidget
+{
+    Q_OBJECT
+signals:
+    void enterExam(CL_OPERATION_TYPE cot, __int64 nExamId, __int64 nExamStudentId);
+public:
+    explicit clOnlineExam(QWidget *parent = nullptr);
+    ~clOnlineExam();
+
+    void setUI(const int nWidth, const int nHeight);
+private slots:
+	void onQueryExamList(CQueryExamList queryExamList);
+	void onQueryExamEndList(CQueryExamEndList queryExamEndList);	
+private:
+	void showEvent(QShowEvent *event);
+
+    Ui::clOnlineExam *ui;
+
+	std::shared_ptr<QTimer> m_pObjectiveScoreTimer;
+    std::shared_ptr<clObjectiveScore> m_pObjectiveScore;
+	std::vector<CExamCourseInfo> m_vOnlieExamList;
+    std::vector<CExamCourseInfo> m_vOnlieEndExamList;
+};
+
+#endif // CLONLINEEXAM_H

+ 116 - 0
client/clOnlineExam.ui

@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clOnlineExam</class>
+ <widget class="QWidget" name="clOnlineExam">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>913</width>
+    <height>604</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QTabWidget" name="tabw_onlineExam">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>451</width>
+     <height>411</height>
+    </rect>
+   </property>
+   <property name="tabPosition">
+    <enum>QTabWidget::North</enum>
+   </property>
+   <property name="tabShape">
+    <enum>QTabWidget::Rounded</enum>
+   </property>
+   <property name="currentIndex">
+    <number>0</number>
+   </property>
+   <widget class="QWidget" name="tab_examList">
+    <attribute name="title">
+     <string>Tab 1</string>
+    </attribute>
+    <layout class="QGridLayout" name="gridLayout_2">
+     <item row="1" column="0">
+      <layout class="QVBoxLayout" name="vlayout_el">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QTableWidget" name="tablewt_examList"/>
+       </item>
+       <item>
+        <widget class="QWidget" name="widget_el_page" native="true">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>62</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>62</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>62</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </widget>
+   <widget class="QWidget" name="tab_finishExamList">
+    <attribute name="title">
+     <string>Tab 2</string>
+    </attribute>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <layout class="QVBoxLayout" name="vlayout_fel">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QTableWidget" name="tablew_finishExamList"/>
+       </item>
+       <item>
+        <widget class="QWidget" name="widget_fel_page" native="true">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>62</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>62</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 215 - 0
client/clOnlineHomework.cpp

@@ -0,0 +1,215 @@
+#include "clOnlineHomework.h"
+#include "ui_clOnlineHomework.h"
+
+#include "CAppInfo.h"
+#include "clOperation.h"
+
+#include <QScrollBar>
+#include "awMsgBox.h"
+
+clOnlineHomework::clOnlineHomework(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clOnlineHomework)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	qRegisterMetaType<CQueryHomeworkList>("CQueryHomeworkList");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnQueryHomeworkList, this, &clOnlineHomework::onQueryHomeworkList);
+
+    m_pObjectiveScoreTimer = std::make_shared<QTimer>();
+    m_pObjectiveScoreTimer->setInterval(200);
+    connect(m_pObjectiveScoreTimer.get(), &QTimer::timeout, this, [&]() {
+        m_pObjectiveScoreTimer->stop();
+        if (m_pObjectiveScore != nullptr)
+        {
+            m_pObjectiveScore.reset();
+        }
+    });
+}
+
+clOnlineHomework::~clOnlineHomework()
+{
+    delete ui;
+}
+
+void clOnlineHomework::setUI(const int nWidth, const int nHeight)
+{
+    setGeometry(0, 0, nWidth, nHeight);
+    ui->tabw_onlineExam->setGeometry(0, 0, nWidth, nHeight);
+    ui->tabw_onlineExam->setTabEnabled(1, false);
+    ui->tabw_onlineExam->setTabText(0, QString::fromLocal8Bit("待考列表"));
+    ui->tabw_onlineExam->setTabText(1, QString::fromLocal8Bit("已完成考试"));
+    ui->vlayout_el->setContentsMargins(g_appInfoPtr->m_fRate*30, 0, g_appInfoPtr->m_fRate*30, 0);
+    ui->vlayout_fel->setContentsMargins(g_appInfoPtr->m_fRate*30, 0, g_appInfoPtr->m_fRate*30, 0);
+
+    ui->tablewt_examList->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablewt_examList->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablewt_examList->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablewt_examList->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablewt_examList->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablewt_examList->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablewt_examList->setRowCount(10);
+    //设置列数
+    ui->tablewt_examList->setColumnCount(7);
+
+    ui->tablewt_examList->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablewt_examList->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+    QStringList sHeadStr;
+    sHeadStr << QString::fromLocal8Bit("课程") <<
+                QString::fromLocal8Bit("层次") <<
+                QString::fromLocal8Bit("专业") <<
+                QString::fromLocal8Bit("考试进入时间") <<
+                QString::fromLocal8Bit("考试时间周期") <<
+                QString::fromLocal8Bit("剩余考试次数") <<
+                QString::fromLocal8Bit("操作");
+
+    ui->tablewt_examList->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablewt_examList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablewt_examList->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablewt_examList->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    ui->tablewt_examList->setColumnWidth(0, g_appInfoPtr->m_fRate*116);
+    ui->tablewt_examList->setColumnWidth(1, g_appInfoPtr->m_fRate*56);
+    ui->tablewt_examList->setColumnWidth(2, g_appInfoPtr->m_fRate*104);
+    ui->tablewt_examList->setColumnWidth(3, g_appInfoPtr->m_fRate*277);
+    ui->tablewt_examList->setColumnWidth(4, g_appInfoPtr->m_fRate*305);
+    ui->tablewt_examList->setColumnWidth(5, g_appInfoPtr->m_fRate*102);
+    ui->tablewt_examList->setColumnWidth(6, g_appInfoPtr->m_fRate*146);
+    ui->tablewt_examList->horizontalHeader()->setStretchLastSection(true);
+
+    ui->tablew_finishExamList->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablew_finishExamList->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablew_finishExamList->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablew_finishExamList->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablew_finishExamList->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablew_finishExamList->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablew_finishExamList->setRowCount(10);
+    //设置列数
+    ui->tablew_finishExamList->setColumnCount(7);
+
+    ui->tablew_finishExamList->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablew_finishExamList->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+
+
+    ui->tablew_finishExamList->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablew_finishExamList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablew_finishExamList->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablew_finishExamList->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    ui->tablew_finishExamList->setColumnWidth(0, g_appInfoPtr->m_fRate*116);
+    ui->tablew_finishExamList->setColumnWidth(1, g_appInfoPtr->m_fRate*56);
+    ui->tablew_finishExamList->setColumnWidth(2, g_appInfoPtr->m_fRate*104);
+    ui->tablew_finishExamList->setColumnWidth(3, g_appInfoPtr->m_fRate*277);
+    ui->tablew_finishExamList->setColumnWidth(4, g_appInfoPtr->m_fRate*305);
+    ui->tablew_finishExamList->setColumnWidth(5, g_appInfoPtr->m_fRate*102);
+    ui->tablew_finishExamList->setColumnWidth(6, g_appInfoPtr->m_fRate*146);
+    ui->tablew_finishExamList->horizontalHeader()->setStretchLastSection(true);
+
+}
+
+void clOnlineHomework::showEvent(QShowEvent *)
+{
+	CHttpRequestPackage hrp;
+	hrp.sUri = "/api/ecs_oe_admin/client/exam/process/queryHomeworkList";
+	hrp.nRequestType = RequestType::rtQueryHomeworkList;
+	g_httpBllPtr->post(hrp);
+}
+
+
+void clOnlineHomework::onQueryHomeworkList(CQueryHomeworkList queryHomeworkList)
+{
+	if (queryHomeworkList.nCode == 200)
+	{
+		int nSize = queryHomeworkList.vHomeworkExamList.size();
+		for (int i = 0; i < nSize; ++i)
+		{
+			CExamCourseInfo eci = queryHomeworkList.vHomeworkExamList[i];
+			ui->tablewt_examList->setItem(i, 0, new QTableWidgetItem(eci.sCourseName));
+			ui->tablewt_examList->setItem(i, 1, new QTableWidgetItem(eci.sCourseLevel));
+			ui->tablewt_examList->setItem(i, 2, new QTableWidgetItem(eci.sSpecialtyName));
+			ui->tablewt_examList->setItem(i, 3, new QTableWidgetItem(QString::fromLocal8Bit("%1 ~ %2").arg(eci.sStartTime).arg(eci.sEndTime)));
+			ui->tablewt_examList->setItem(i, 4, new QTableWidgetItem(QString::fromLocal8Bit("")));
+			ui->tablewt_examList->setItem(i, 5, new QTableWidgetItem(QString::number(eci.nAllowExamCount)));
+			clOperation *clo = new clOperation(i, ui->tablewt_examList);
+            connect(clo, &clOperation::enterExam, this, [&](CL_OPERATION_TYPE cot, int nRow){
+                if (cot == CL_OPERATION_TYPE::cot_online_homework )
+                {
+                    emit enterExam(cot, m_vHomeworkExamList[nRow].nExamId, m_vHomeworkExamList[nRow].nExamStudentId);
+                }
+            });
+
+            connect(clo, &clOperation::showObjectiveScore, this, [&](CL_OPERATION_TYPE cot, int nRow) {
+                if (cot == CL_OPERATION_TYPE::cot_online_homework )
+                {
+                    m_pObjectiveScoreTimer->stop();
+
+                    if (m_pObjectiveScore != nullptr &&
+                        m_pObjectiveScore->getRow() != nRow)
+                    {
+                        m_pObjectiveScore.reset();
+                    }
+
+                    if (m_pObjectiveScore == nullptr)
+                    {
+                        m_pObjectiveScore = std::make_shared<clObjectiveScore>(nRow, QString::number(m_vHomeworkExamList[nRow].nExamStudentId), this);
+                        connect(m_pObjectiveScore.get(), &clObjectiveScore::objectiveScoreHeight, this, [&](int nRow, int nHeight) {
+                            int ntableTop =  ui->tabw_onlineExam->y() + g_appInfoPtr->m_fRate*70;
+                            int nTop = ntableTop + (nRow + 2)* ui->tablewt_examList->verticalHeader()->defaultSectionSize() - nHeight / 2;
+                            if (nTop < ui->tabw_onlineExam->y())
+                            {
+                                nTop = ui->tabw_onlineExam->y();
+                            }
+                            else if ((nTop + nHeight) > (ntableTop + ui->tablewt_examList->height()))
+                            {
+                                nTop = ntableTop + ui->tablewt_examList->height() - nHeight;
+                            }
+                            m_pObjectiveScore->setUI(ui->tablewt_examList->x() + ui->tablewt_examList->width() - ui->tablewt_examList->columnWidth(6) - g_appInfoPtr->m_fRate * 530,
+                                nTop, g_appInfoPtr->m_fRate * 530, nHeight);
+                            m_pObjectiveScore->show();
+                        });
+
+                    }
+                }
+
+            });
+
+            connect(clo, &clOperation::hideObjectiveScore, this, [&](CL_OPERATION_TYPE cot, int nRow) {
+                if (cot == CL_OPERATION_TYPE::cot_online_homework)
+                {
+                    m_pObjectiveScoreTimer->start();
+                }
+            });
+
+			clo->setUI(ui->tablewt_examList->columnWidth(6), ui->tablewt_examList->rowHeight(i), CL_OPERATION_TYPE::cot_online_homework);
+			ui->tablewt_examList->setCellWidget(i, 6, clo);
+		}
+        m_vHomeworkExamList.swap(queryHomeworkList.vHomeworkExamList);
+	}
+	else
+	{
+        if(queryHomeworkList.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取在线作业列表失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(queryHomeworkList.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}

+ 37 - 0
client/clOnlineHomework.h

@@ -0,0 +1,37 @@
+#ifndef CLONLINEHOMEWORK_H
+#define CLONLINEHOMEWORK_H
+
+#include <QWidget>
+#include <QTimer>
+#include "clObjectiveScore.h"
+#include "CHttpBll.h"
+#include "clOperation.h"
+
+namespace Ui {
+class clOnlineHomework;
+}
+
+class clOnlineHomework : public QWidget
+{
+    Q_OBJECT
+signals:
+    void enterExam(CL_OPERATION_TYPE cot, __int64 nExamId, __int64 nExamStudentId);
+public:
+    explicit clOnlineHomework(QWidget *parent = nullptr);
+    ~clOnlineHomework();
+
+    void setUI(const int nWidth, const int nHeight);
+
+private slots:
+	void onQueryHomeworkList(CQueryHomeworkList queryHomeworkList);
+private:
+	void clOnlineHomework::showEvent(QShowEvent *);
+
+    Ui::clOnlineHomework *ui;
+    std::vector<CExamCourseInfo> m_vHomeworkExamList;
+
+    std::shared_ptr<QTimer> m_pObjectiveScoreTimer;
+    std::shared_ptr<clObjectiveScore> m_pObjectiveScore;
+};
+
+#endif // CLONLINEHOMEWORK_H

+ 116 - 0
client/clOnlineHomework.ui

@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clOnlineHomework</class>
+ <widget class="QWidget" name="clOnlineHomework">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>901</width>
+    <height>599</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QTabWidget" name="tabw_onlineExam">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>451</width>
+     <height>411</height>
+    </rect>
+   </property>
+   <property name="tabPosition">
+    <enum>QTabWidget::North</enum>
+   </property>
+   <property name="tabShape">
+    <enum>QTabWidget::Rounded</enum>
+   </property>
+   <property name="currentIndex">
+    <number>1</number>
+   </property>
+   <widget class="QWidget" name="tab_examList">
+    <attribute name="title">
+     <string>Tab 1</string>
+    </attribute>
+    <layout class="QGridLayout" name="gridLayout_2">
+     <item row="1" column="0">
+      <layout class="QVBoxLayout" name="vlayout_el">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QTableWidget" name="tablewt_examList"/>
+       </item>
+       <item>
+        <widget class="QWidget" name="widget_el_page" native="true">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>62</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>62</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>62</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </widget>
+   <widget class="QWidget" name="tab_finishExamList">
+    <attribute name="title">
+     <string>Tab 2</string>
+    </attribute>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <layout class="QVBoxLayout" name="vlayout_fel">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QTableWidget" name="tablew_finishExamList"/>
+       </item>
+       <item>
+        <widget class="QWidget" name="widget_fel_page" native="true">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>62</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>62</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 190 - 0
client/clOnlinePractice.cpp

@@ -0,0 +1,190 @@
+#include "clOnlinePractice.h"
+#include "ui_clOnlinePractice.h"
+
+#include "CAppInfo.h"
+#include "clOperation.h"
+
+#include <QScrollBar>
+#include <QListView>
+#include "awMsgBox.h"
+
+clOnlinePractice::clOnlinePractice(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clOnlinePractice)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	qRegisterMetaType<CQueryBatchList>("CQueryBatchList");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnQueryBatchList, this, &clOnlinePractice::onQueryBatchList);
+	qRegisterMetaType<CQueryPracticeCourseList>("CQueryPracticeCourseList");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnQueryPracticeCourseList, this, &clOnlinePractice::onQueyPracticeCourseList);
+	
+}
+
+clOnlinePractice::~clOnlinePractice()
+{
+    delete ui;
+}
+
+void clOnlinePractice::setUI(const int nWidth, const int nHeight)
+{
+    setGeometry(0, 0, nWidth, nHeight);
+    ui->widget_onlinePractice->setGeometry(0, 0, width(), height());
+    ui->widget_op_top->setGeometry(0, 0, width(), g_appInfoPtr->m_fRate*72);
+    ui->label_batch->adjustSize();
+    ui->label_batch->setGeometry(g_appInfoPtr->m_fRate*30, (ui->widget_op_top->height() - ui->label_batch->height())/2,
+                                 ui->label_batch->width(), ui->label_batch->height());
+    ui->cbo_batch->setGeometry(ui->label_batch->x() + ui->label_batch->width() + g_appInfoPtr->m_fRate*10,
+                               (ui->widget_op_top->height() - g_appInfoPtr->m_fRate*32)/2,
+                               g_appInfoPtr->m_fRate*240, g_appInfoPtr->m_fRate*32);
+	ui->cbo_batch->setView(new QListView);
+	ui->tablewt_examList->setGeometry(g_appInfoPtr->m_fRate*30, ui->widget_op_top->height() + g_appInfoPtr->m_fRate*10,
+                                      ui->widget_onlinePractice->width() - g_appInfoPtr->m_fRate*30*2,
+                                      ui->widget_onlinePractice->height() - ui->widget_op_top->height() - g_appInfoPtr->m_fRate*(10 + 62));
+    ui->tablewt_examList->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablewt_examList->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablewt_examList->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablewt_examList->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablewt_examList->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablewt_examList->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablewt_examList->setRowCount(10);
+    //设置列数
+    ui->tablewt_examList->setColumnCount(8);
+
+    ui->tablewt_examList->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablewt_examList->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+    QStringList sHeadStr;
+    sHeadStr << QString::fromLocal8Bit("课程") <<
+                QString::fromLocal8Bit("考试进入时间") <<
+                QString::fromLocal8Bit("考试时间周期") <<
+                QString::fromLocal8Bit("练习次数") <<
+                QString::fromLocal8Bit("最近正确率") <<
+                QString::fromLocal8Bit("平均正确率") <<
+                QString::fromLocal8Bit("最高正确率") <<
+                QString::fromLocal8Bit("操作");
+
+    ui->tablewt_examList->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablewt_examList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablewt_examList->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablewt_examList->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    ui->tablewt_examList->setColumnWidth(0, g_appInfoPtr->m_fRate*116);
+    ui->tablewt_examList->setColumnWidth(1, g_appInfoPtr->m_fRate*275);
+    ui->tablewt_examList->setColumnWidth(2, g_appInfoPtr->m_fRate*280);
+    ui->tablewt_examList->setColumnWidth(3, g_appInfoPtr->m_fRate*66);
+    ui->tablewt_examList->setColumnWidth(4, g_appInfoPtr->m_fRate*80);
+    ui->tablewt_examList->setColumnWidth(5, g_appInfoPtr->m_fRate*80);
+    ui->tablewt_examList->setColumnWidth(6, g_appInfoPtr->m_fRate*80);
+    ui->tablewt_examList->setColumnWidth(7, g_appInfoPtr->m_fRate*146);
+    ui->tablewt_examList->horizontalHeader()->setStretchLastSection(true);
+
+
+}
+
+void clOnlinePractice::onQueryBatchList(CQueryBatchList queryBatchList)
+{
+	if (queryBatchList.nCode == 200)
+	{
+		ui->cbo_batch->clear();
+		for (CBatchInfo bi : queryBatchList.vBatchList)
+		{
+			ui->cbo_batch->addItem(bi.sName, bi.nId);
+		}
+		ui->cbo_batch->setCurrentIndex(0);
+	}
+	else
+	{
+        if(queryBatchList.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取批次信息失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(queryBatchList.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clOnlinePractice::showEvent(QShowEvent *)
+{
+	CHttpRequestPackage hrp;
+	hrp.sUri = "/api/ecs_exam_work/exam/queryByNameLike";
+	hrp.sParamList.push_back("examTypes,PRACTICE");
+	hrp.sParamList.push_back(QString("studentId,%1").arg(g_appInfoPtr->m_nStudentId));
+	hrp.sParamList.push_back(QString("name,%1").arg(""));
+	hrp.nRequestType = RequestType::rtQueryBatchList;
+	g_httpBllPtr->get(hrp);
+}
+
+void clOnlinePractice::onQueyPracticeCourseList(CQueryPracticeCourseList queryPracticeCourseList)
+{
+	if (queryPracticeCourseList.nCode == 200)
+	{
+		int nSize = queryPracticeCourseList.vPracticeCourseInfo.size();
+		for (int i = 0; i < nSize; ++i)
+		{
+			CPracticeCourseInfo pci = queryPracticeCourseList.vPracticeCourseInfo[i];
+			ui->tablewt_examList->setItem(i, 0, new QTableWidgetItem(pci.sCourseName));
+			ui->tablewt_examList->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("%1 ~ %2").arg(pci.sStartTime).arg(pci.sEndTime)));
+			ui->tablewt_examList->setItem(i, 2, new QTableWidgetItem(QString::fromLocal8Bit("")));
+			ui->tablewt_examList->setItem(i, 3, new QTableWidgetItem(QString::number(pci.nPracticeCount)));
+			ui->tablewt_examList->setItem(i, 4, new QTableWidgetItem(QString::fromLocal8Bit("%1%").arg(pci.fRecentObjectiveAccuracy)));
+			ui->tablewt_examList->setItem(i, 5, new QTableWidgetItem(QString::fromLocal8Bit("%1%").arg(pci.fAveObjectiveAccuracy)));
+			ui->tablewt_examList->setItem(i, 6, new QTableWidgetItem(QString::fromLocal8Bit("%1%").arg(pci.fMaxObjectiveAccuracy)));
+			clOperation *clo = new clOperation(i, ui->tablewt_examList);
+
+            connect(clo, &clOperation::enterExam, this, [&](CL_OPERATION_TYPE cot, int nRow){
+                if (cot == CL_OPERATION_TYPE::cot_online_practice )
+                {
+                    emit enterExam(cot, m_vPracticeCourseInfo[nRow].nExamId, m_vPracticeCourseInfo[nRow].nExamStudentId);
+                }
+            });
+            connect(clo, &clOperation::viewPracticeDetail, this, [&](int nRow){
+                if(m_pResultList == nullptr)
+                {
+                    m_pResultList = std::make_shared<clopResultList>(m_vPracticeCourseInfo[nRow],  ui->cbo_batch->currentText(), this);
+                    connect(m_pResultList.get(), &clopResultList::goback, this, [&](){
+                        m_pResultList.reset();
+                    });
+                    m_pResultList->setUI(0, 0, width(), height());
+                }
+
+                m_pResultList->show();
+            });
+			clo->setUI(ui->tablewt_examList->columnWidth(7), ui->tablewt_examList->rowHeight(i), CL_OPERATION_TYPE::cot_online_practice);
+			ui->tablewt_examList->setCellWidget(i, 7, clo);
+		}
+        m_vPracticeCourseInfo.swap(queryPracticeCourseList.vPracticeCourseInfo);
+	}
+	else
+	{
+        if(queryPracticeCourseList.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取练习列表失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(queryPracticeCourseList.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clOnlinePractice::on_cbo_batch_currentIndexChanged(int index)
+{
+    QString sExamId = ui->cbo_batch->currentData().toString();
+	CHttpRequestPackage hrp;
+	hrp.sUri = "/api/ecs_oe_admin/client/exam/process/queryPracticeCourseList";
+	hrp.sParamList.push_back(QString("examId,%1").arg(sExamId));
+	hrp.nRequestType = RequestType::rtQueryPracticeCourseList;
+	hrp.eParamType = HttpParamType::hptUrl;
+	g_httpBllPtr->post(hrp);
+}

+ 36 - 0
client/clOnlinePractice.h

@@ -0,0 +1,36 @@
+#ifndef CLONLINEPRACTICE_H
+#define CLONLINEPRACTICE_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+#include "clopResultList.h"
+#include "clOperation.h"
+namespace Ui {
+class clOnlinePractice;
+}
+
+class clOnlinePractice : public QWidget
+{
+    Q_OBJECT
+signals:
+    void enterExam(CL_OPERATION_TYPE cot, __int64 nExamId, __int64 nExamStudentId);
+public:
+    explicit clOnlinePractice(QWidget *parent = nullptr);
+    ~clOnlinePractice();
+
+    void setUI(const int nWidth, const int nHeight);
+private slots:
+	void onQueryBatchList(CQueryBatchList queryBatchList);
+	void onQueyPracticeCourseList(CQueryPracticeCourseList queryPracticeCourseList);
+    void on_cbo_batch_currentIndexChanged(int index);
+
+private:
+	void showEvent(QShowEvent *);
+    Ui::clOnlinePractice *ui;
+
+    std::vector<CPracticeCourseInfo> m_vPracticeCourseInfo;
+
+    std::shared_ptr<clopResultList> m_pResultList;
+};
+
+#endif // CLONLINEPRACTICE_H

+ 78 - 0
client/clOnlinePractice.ui

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clOnlinePractice</class>
+ <widget class="QWidget" name="clOnlinePractice">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>866</width>
+    <height>571</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_onlinePractice" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>871</width>
+     <height>571</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_op_top" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>931</width>
+      <height>80</height>
+     </rect>
+    </property>
+    <property name="styleSheet">
+     <string notr="true"/>
+    </property>
+    <widget class="QLabel" name="label_batch">
+     <property name="geometry">
+      <rect>
+       <x>40</x>
+       <y>30</y>
+       <width>81</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>选择考试批次</string>
+     </property>
+    </widget>
+    <widget class="QComboBox" name="cbo_batch">
+     <property name="geometry">
+      <rect>
+       <x>120</x>
+       <y>30</y>
+       <width>191</width>
+       <height>22</height>
+      </rect>
+     </property>
+     <property name="currentText">
+      <string/>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QTableWidget" name="tablewt_examList">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>80</y>
+      <width>871</width>
+      <height>401</height>
+     </rect>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 81 - 0
client/clStudentInfo.cpp

@@ -0,0 +1,81 @@
+#include "clStudentInfo.h"
+#include "ui_clStudentInfo.h"
+
+#include "CAppInfo.h"
+
+#include <QGraphicsDropShadowEffect>
+
+clStudentInfo::clStudentInfo(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clStudentInfo)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+}
+
+clStudentInfo::~clStudentInfo()
+{
+    delete ui;
+}
+
+void clStudentInfo::setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight)
+{
+    setGeometry(nLeft, nTop, nWidth, nHeight);
+    ui->label_clsi_studentName->setText(g_appInfoPtr->m_sStudentName);
+    ui->label_clsi_orgName->setText(g_appInfoPtr->m_sOrgName);
+    ui->label_clsi_studentCode->setText(g_appInfoPtr->m_sStudentCode);
+    ui->label_clsi_identity->setText(g_appInfoPtr->m_sStudentIdentityNumber);
+    QString sFileName = g_appInfoPtr->m_sStudentPhotoPath.right(g_appInfoPtr->m_sStudentPhotoPath.length() - g_appInfoPtr->m_sStudentPhotoPath.lastIndexOf("/") - 1);
+    sFileName = g_appInfoPtr->m_sCacheFileDir + sFileName;
+
+    ui->widget_npw_BG->setGeometry(g_appInfoPtr->m_fRate*5, g_appInfoPtr->m_fRate*5, width()-g_appInfoPtr->m_fRate*10, height()-g_appInfoPtr->m_fRate*10);
+
+    ui->label_studentPhoto->setGeometry(ui->widget_npw_BG->width() - g_appInfoPtr->m_fRate*20 - g_appInfoPtr->m_fRate*64, g_appInfoPtr->m_fRate*20,
+                                        g_appInfoPtr->m_fRate*64, g_appInfoPtr->m_fRate*80);
+    ui->label_studentPhoto->setPixmap(QPixmap(sFileName).scaled(ui->label_studentPhoto->width(), ui->label_studentPhoto->height(),
+                                                                Qt::IgnoreAspectRatio));
+    ui->label_clsi_studentName->adjustSize();
+    ui->label_clsi_studentName->setGeometry(g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*20,
+                                            ui->label_clsi_studentName->width(), ui->label_clsi_studentName->height());
+    ui->label_clsi_studentCodeHint->adjustSize();
+    ui->label_clsi_studentCodeHint->setGeometry(ui->label_clsi_studentName->x(), ui->label_clsi_studentName->y() + ui->label_clsi_studentName->height() + g_appInfoPtr->m_fRate*20,
+                                                ui->label_clsi_studentCodeHint->width(), ui->label_clsi_studentCodeHint->height());
+    ui->label_clsi_studentCode->adjustSize();
+    ui->label_clsi_studentCode->setGeometry(ui->label_clsi_studentCodeHint->x() + ui->label_clsi_studentCodeHint->width() + g_appInfoPtr->m_fRate*34,
+                                            ui->label_clsi_studentCodeHint->y(), ui->label_clsi_studentCode->width(), ui->label_clsi_studentCode->height());
+    ui->label_clsi_identityHint->adjustSize();
+    ui->label_clsi_identityHint->setGeometry(ui->label_clsi_studentName->x(), ui->label_clsi_studentCodeHint->y() + ui->label_clsi_studentCodeHint->height() + g_appInfoPtr->m_fRate*10,
+                                             ui->label_clsi_identityHint->width(), ui->label_clsi_identityHint->height());
+    ui->label_clsi_identity->adjustSize();
+    ui->label_clsi_identity->setGeometry(ui->label_clsi_studentCode->x(), ui->label_clsi_identityHint->y(),
+                                         ui->label_clsi_identity->width(), ui->label_clsi_identity->height());
+    ui->label_clsi_orgNameHint->adjustSize();
+    ui->label_clsi_orgNameHint->setGeometry(ui->label_clsi_studentName->x(), ui->label_clsi_identityHint->y() + ui->label_clsi_identityHint->height() + g_appInfoPtr->m_fRate*10,
+                                            ui->label_clsi_orgNameHint->width(), ui->label_clsi_orgNameHint->height());
+    ui->label_clsi_orgName->adjustSize();
+    ui->label_clsi_orgName->setGeometry(ui->label_clsi_studentCode->x(), ui->label_clsi_orgNameHint->y(),
+                                        ui->label_clsi_orgName->width(), ui->label_clsi_orgName->height());
+    ui->label_HLine->setGeometry(ui->label_clsi_studentName->x(), ui->label_clsi_orgNameHint->y() + ui->label_clsi_orgNameHint->height() + g_appInfoPtr->m_fRate*20,
+                                 ui->widget_npw_BG->width() - g_appInfoPtr->m_fRate*20*2,g_appInfoPtr->m_fRate*1);
+    ui->btn_clsi_editPassword->setGeometry(ui->label_clsi_studentName->x(), ui->label_HLine->y() + ui->label_HLine->height() + g_appInfoPtr->m_fRate*20,
+                                      g_appInfoPtr->m_fRate*135, g_appInfoPtr->m_fRate*32);
+    ui->btn_clsi_exitLogin->setGeometry(ui->widget_npw_BG->width() - g_appInfoPtr->m_fRate*20 - ui->btn_clsi_editPassword->width(),
+                                   ui->btn_clsi_editPassword->y(), ui->btn_clsi_editPassword->width(), ui->btn_clsi_editPassword->height());
+
+    QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect;
+    effect->setOffset(g_appInfoPtr->m_fRate*4,g_appInfoPtr->m_fRate*4);
+    effect->setColor(QColor(0,0,0,30));
+    effect->setBlurRadius(g_appInfoPtr->m_fRate*16);
+    ui->widget_npw_BG->setGraphicsEffect(effect);
+}
+
+void clStudentInfo::on_btn_clsi_editPassword_clicked()
+{
+    emit sgnStudentEditPassword();
+}
+
+void clStudentInfo::on_btn_clsi_exitLogin_clicked()
+{
+    emit sgnStudentLogout();
+}

+ 30 - 0
client/clStudentInfo.h

@@ -0,0 +1,30 @@
+#ifndef CLSTUDENTINFO_H
+#define CLSTUDENTINFO_H
+
+#include <QWidget>
+
+namespace Ui {
+class clStudentInfo;
+}
+
+class clStudentInfo : public QWidget
+{
+    Q_OBJECT
+signals:
+    void sgnStudentEditPassword();
+    void sgnStudentLogout();
+public:
+    explicit clStudentInfo(QWidget *parent = nullptr);
+    ~clStudentInfo();
+
+    void setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight);
+private slots:
+    void on_btn_clsi_editPassword_clicked();
+
+    void on_btn_clsi_exitLogin_clicked();
+
+private:
+    Ui::clStudentInfo *ui;
+};
+
+#endif // CLSTUDENTINFO_H

+ 175 - 0
client/clStudentInfo.ui

@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clStudentInfo</class>
+ <widget class="QWidget" name="clStudentInfo">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_npw_BG" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>401</width>
+     <height>301</height>
+    </rect>
+   </property>
+   <widget class="QPushButton" name="btn_clsi_editPassword">
+    <property name="geometry">
+     <rect>
+      <x>90</x>
+      <y>240</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>修改密码</string>
+    </property>
+   </widget>
+   <widget class="QPushButton" name="btn_clsi_exitLogin">
+    <property name="geometry">
+     <rect>
+      <x>200</x>
+      <y>240</y>
+      <width>75</width>
+      <height>23</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>退出登录</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_studentPhoto">
+    <property name="geometry">
+     <rect>
+      <x>280</x>
+      <y>30</y>
+      <width>91</width>
+      <height>131</height>
+     </rect>
+    </property>
+    <property name="styleSheet">
+     <string notr="true"/>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_clsi_studentName">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>30</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>何乐乐</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_clsi_studentCodeHint">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>80</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>学号</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_clsi_identityHint">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>120</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>身份证号</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_clsi_orgNameHint">
+    <property name="geometry">
+     <rect>
+      <x>30</x>
+      <y>160</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>学习中心</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_clsi_studentCode">
+    <property name="geometry">
+     <rect>
+      <x>110</x>
+      <y>80</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>T20190123</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_clsi_identity">
+    <property name="geometry">
+     <rect>
+      <x>110</x>
+      <y>120</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>402000201010202827</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_clsi_orgName">
+    <property name="geometry">
+     <rect>
+      <x>110</x>
+      <y>160</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>测试中心</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_HLine">
+    <property name="geometry">
+     <rect>
+      <x>50</x>
+      <y>200</y>
+      <width>54</width>
+      <height>12</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string/>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 160 - 0
client/client.pro

@@ -0,0 +1,160 @@
+QT       += core gui webenginewidgets multimedia
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+CONFIG += c++17
+
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+    answerWidget.cpp \
+    awBackground.cpp \
+    awExamScore.cpp \
+    awHandinPaper.cpp \
+    awMsgBox.cpp \
+    awQuestionNavigate.cpp \
+    awResumeExam.cpp \
+    awWaitExam.cpp \
+    awqn_item.cpp \
+    clEditPassword.cpp \
+    clMobileLogin.cpp \
+    clNoticeDetail.cpp \
+    clNoticeList.cpp \
+    clNoticePopWidget.cpp \
+    clObjectiveScore.cpp \
+    clOfflineExam.cpp \
+    clOnlineExam.cpp \
+    clOnlineHomework.cpp \
+    clOnlinePractice.cpp \
+    clStudentInfo.cpp \
+    cloeUploadFile.cpp \
+    cloeViewPaper.cpp \
+    clopPaperDetail.cpp \
+    clopPaperReport.cpp \
+    clopReport.cpp \
+    clopResultList.cpp \
+    courseList.cpp \
+    environmentalTest.cpp \
+    etCameraTest.cpp \
+    etMobileTest.cpp \
+    etNetworkTest.cpp \
+    etTestResult.cpp \
+    etTimeTest.cpp \
+    etVoiceTest.cpp \
+    etWhetherEnvTest.cpp \
+    main.cpp \
+    login.cpp \
+    privacyWidget.cpp \
+    welcomeWidget.cpp
+
+HEADERS += \
+    answerWidget.h \
+    awBackground.h \
+    awExamScore.h \
+    awHandinPaper.h \
+    awMsgBox.h \
+    awQuestionNavigate.h \
+    awResumeExam.h \
+    awWaitExam.h \
+    awqn_item.h \
+    clEditPassword.h \
+    clMobileLogin.h \
+    clNoticeDetail.h \
+    clNoticeList.h \
+    clNoticePopWidget.h \
+    clObjectiveScore.h \
+    clOfflineExam.h \
+    clOnlineExam.h \
+    clOnlineHomework.h \
+    clOnlinePractice.h \
+    clStudentInfo.h \
+    cloeUploadFile.h \
+    cloeViewPaper.h \
+    clopPaperDetail.h \
+    clopPaperReport.h \
+    clopReport.h \
+    clopResultList.h \
+    courseList.h \
+    environmentalTest.h \
+    etCameraTest.h \
+    etMobileTest.h \
+    etNetworkTest.h \
+    etTestResult.h \
+    etTimeTest.h \
+    etVoiceTest.h \
+    etWhetherEnvTest.h \
+    login.h \
+    privacyWidget.h \
+    welcomeWidget.h
+
+FORMS += \
+    answerWidget.ui \
+    awBackground.ui \
+    awExamScore.ui \
+    awHandinPaper.ui \
+    awMsgBox.ui \
+    awQuestionNavigate.ui \
+    awResumeExam.ui \
+    awWaitExam.ui \
+    awqn_item.ui \
+    clEditPassword.ui \
+    clMobileLogin.ui \
+    clNoticeDetail.ui \
+    clNoticeList.ui \
+    clNoticePopWidget.ui \
+    clObjectiveScore.ui \
+    clOfflineExam.ui \
+    clOnlineExam.ui \
+    clOnlineHomework.ui \
+    clOnlinePractice.ui \
+    clStudentInfo.ui \
+    cloeUploadFile.ui \
+    cloeViewPaper.ui \
+    clopPaperDetail.ui \
+    clopPaperReport.ui \
+    clopReport.ui \
+    clopResultList.ui \
+    courseList.ui \
+    environmentalTest.ui \
+    etCameraTest.ui \
+    etMobileTest.ui \
+    etNetworkTest.ui \
+    etTestResult.ui \
+    etTimeTest.ui \
+    etVoiceTest.ui \
+    etWhetherEnvTest.ui \
+    login.ui \
+    privacyWidget.ui \
+    welcomeWidget.ui
+
+UI_DIR += ./forms_client
+RCC_DIR += ./tmp_client
+MOC_DIR += ./tmp_client
+OBJECTS_DIR += ./tmp_client
+
+include(../common/common.pri)
+include(../component/component.pri)
+include(../face/face.pri)
+include(../question/question.pri)
+
+INCLUDEPATH += ../common/ \
+    ../component/ \
+    ../face/ \
+    ../question/ \
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+RESOURCES += \
+    rc.qrc

+ 1957 - 0
client/client.qss

@@ -0,0 +1,1957 @@
+QWidget#widget_BG
+{
+    border-image:url(:/images/img-login-bg.png);
+}
+
+QPushButton#btn_close
+{
+    outline:none;
+    border-image:url(:/images/btn-close.png);
+}
+
+QWidget#widget_loginBG
+{
+    border-radius:RATE_BASE_SIZE20px;
+}
+
+QWidget#widget_orgInfo
+{
+    border-image:url(:/images/img-login-org-info-bg.png);
+}
+
+QLabel#label_orgTitle
+{
+    font-size:RATE_BASE_SIZE22px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QLabel#label_complay, #label_version
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,0.7);
+}
+
+QWidget#widget_login
+{
+    background:rgba(255,255,255,1);
+    border-top-right-radius:RATE_BASE_SIZE20px;
+    border-bottom-right-radius:RATE_BASE_SIZE20px;
+}
+
+QPushButton#btn_studentCodeLogin
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+    background:rgba(239,240,245,1);
+    border:0px;
+}
+
+QPushButton#btn_identityLogin
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+    background:rgba(239,240,245,1);
+    border-top-right-radius:RATE_BASE_SIZE20px;
+}
+
+QLabel#label_loginTitle
+{
+    font-size:RATE_BASE_SIZE20px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(153,153,153,1);
+}
+
+QLineEdit#edt_account
+{
+    background:rgba(239,240,245,1);
+    border:RATE_BASE_SIZE1px solid rgba(228,229,235,1);
+    border-radius:RATE_BASE_SIZE10px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+    padding-left:RATE_BASE_SIZE39px;
+    border-image:url(:/images/edt-account.png);
+}
+
+QLineEdit#edt_password
+{
+    background:rgba(239,240,245,1);
+    border:RATE_BASE_SIZE1px solid rgba(228,229,235,1);
+    border-radius:RATE_BASE_SIZE10px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+    padding-left:RATE_BASE_SIZE39px;
+    border-image:url(:/images/edt-password.png);
+}
+
+QPushButton#btn_login
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+    background:rgba(19,187,138,1);
+    border:0px;
+    border-radius:RATE_BASE_SIZE10px;
+}
+
+QWidget#widget_pmb_bg,#widget_wcw_BG,#widget_pw_BG,#widget_et_BG,#widget_awhp_BG,#widget_awre_BG,#widget_awmb_BG
+{
+    background:rgba(255, 255, 255, 1);
+    border-radius:RATE_BASE_SIZE16px;
+}
+
+QLabel#label_pmb_icon,#label_awmb_icon
+{
+    border-image:url(:/images/icon-um.png);
+}
+
+QLabel#label_pmb_title
+{
+    font-size:RATE_BASE_SIZE18px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51,51,51,1);
+}
+
+QLabel#label_pmb_content
+{
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QPushButton#btn_yes,#btn_detail,#btn_wcw_comfirm,#btn_startEnvTest
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+    background:rgba(19,186,140,1);
+    border-radius:RATE_BASE_SIZE6px;
+}
+
+QPushButton#btn_no,#btn_ignore,#btn_skipEnvTest
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+    background:rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE6px;
+}
+
+QWidget#widget_cl_BG,#widget_clnd_client,#widget_clop_BG
+{
+    background:rgba(255,255,255,1);
+}
+
+
+QWidget#widget_menu
+{
+    border-image:url(:/images/img-cl-menu.png);
+}
+
+QPushButton#btn_onlineExam,#btn_onlineHomework,#btn_onlinePractice,#btn_offlineExam,#btn_notice,#btn_editPassword
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,0.4);
+    background:rgba(255,255,255,0);
+    border-radius:RATE_BASE_SIZE12px;
+}
+
+QPushButton#btn_noticeNum
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE11px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+    background:rgba(254,119,100,1);
+    border-radius:RATE_BASE_SIZE8px;
+}
+
+QWidget#widget_top
+{
+    background:rgba(255,255,255,1);
+    border-bottom:1px solid rgba(239,240,245,1);
+}
+
+QWidget#widget_bottom
+{
+    background:rgba(255,255,255,1);
+    border-top:1px solid rgba(239,240,245,1);
+}
+
+QLabel#label_currentPlaceIcon
+{
+    border-image:url(:/images/icon-current-place.png);
+}
+
+QPushButton#btn_cl_close
+{
+    outline:none;
+    border-image:url(:/images/icon-cl-close.png);
+}
+
+QPushButton#btn_minisize
+{
+    outline:none;
+    border-image:url(:/images/icon-cl-minisize.png);
+}
+
+QLabel#label_VLine,#label_HLine,#label_HLine_top,#label_HLine_bottom,#line_question
+{
+    background:rgba(239,240,245,1);
+}
+
+QPushButton#btn_exit
+{
+    outline:none;
+    border-image:url(:/images/icon-exit.png);
+}
+
+QPushButton#btn_studentInfo
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(134,135,141,1);
+    background:rgba(239,240,245,1);
+    border-radius:RATE_BASE_SIZE12px;
+}
+
+QPushButton#btn_studentInfo,#btn_mobileLogin
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(134,135,141,1);
+    background:rgba(239,240,245,1);
+    border-radius:RATE_BASE_SIZE12px;
+}
+
+QPushButton#btn_mobileLogin:hover,#btn_studentInfo:hover
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(255,255,255,1);
+    background:rgba(19,187,138,1);
+    border-radius:RATE_BASE_SIZE12px;
+}
+
+
+QLabel#label_currrentPlace,#label_cl_version,#label_serverTime,#label_cl_company,#label_batch
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(153,153,153,1);
+}
+
+QTabWidget::pane
+{
+    border-top:1px solid rgba(239,240,245,1);
+    top: RATE_BASE_SIZE40px;
+}
+
+QTabWidget::tab-bar
+{
+    left:RATE_BASE_SIZE30px;
+    top:RATE_BASE_SIZE20px;
+}
+
+
+QTabBar::tab
+{
+    height:RATE_BASE_SIZE30px;
+    width:RATE_BASE_SIZE86px;
+    margin:RATE_BASE_SIZE7px;
+    background: rgba(239,240,245,1);
+    border-radius: RATE_BASE_SIZE15px;
+
+    color:rgba(153,153,153,1);
+}
+
+
+QTabBar::tab:selected
+{
+    color:rgba(255,255,255,1);
+    background: rgba(19,187,138,1);
+}
+
+QTabBar::tab:disabled
+{
+    width:0px;
+    color:transparent;
+}
+
+
+QHeaderView
+{
+    background:transparent;
+    border:0px solid rgb(255,0,0);
+    border-bottom:RATE_BASE_SIZE1px solid rgba(239,240,245,1);
+
+}
+
+QHeaderView::section
+{
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    color:rgba(153,153,153,1);
+    font-weight:600;
+
+    background:#FFFFFF;
+
+    min-height:RATE_BASE_SIZE48px;
+    max-height:RATE_BASE_SIZE48px;
+
+
+    border:0px solid rgb(255,0,0);
+    padding-left:0px;
+}
+
+QTableWidget
+{
+    outline:none;
+    border:0px solid rgb(255,0,0);
+
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(75, 82, 96,1);
+
+}
+
+
+QTableWidget::item
+{
+    outline:none;
+    border:0px solid rgb(255,0,0);
+    border-bottom:1px solid rgba(239,240,245,1);
+
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(51, 51, 51,1);
+    padding-left:0px;
+}
+
+
+QTableWidget::item::selected
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(51, 51, 51,1);
+}
+
+QPushButton#btn1,#btn2,#btn3
+{
+    outline:none;
+    border:1px solid rgba(148,219,197,1);
+    border-radius:RATE_BASE_SIZE6px;
+    background:rgba(255,255,255,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(19, 186, 140,1);
+}
+
+QPushButton#btn1:hover,#btn2:hover,#btn3:hover
+{
+    outline:none;
+    border:0px solid rgba(148,219,197,1);
+    border-radius:RATE_BASE_SIZE6px;
+    background:rgba(19, 187, 138,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(255,255,255,1);
+}
+
+QWidget#widget_op_top
+{
+    background:rgba(239,240,245,1);
+}
+
+QComboBox
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(51,51,51,1);
+    padding-left: RATE_BASE_SIZE16px;
+    background:rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE10px;
+    border:RATE_BASE_SIZE1px solid rgba(228,229,235,1);
+}
+
+QComboBox:on
+{
+    outline:none;
+}
+
+QComboBox:on
+{
+    outline:none;
+}
+
+/* 右侧下拉按钮*/
+QComboBox:drop-down
+{
+
+    width: 0px;
+}
+
+/* 右侧下拉按钮 图标*/
+QComboBox:down-arrow
+{
+    width: 0px;
+}
+
+/* list区域*/
+QComboBox QAbstractItemView
+{
+     selection-background-color: rgba(248,250,252,1);
+     background:rgba(248,250,252,1);
+     border-radius:RATE_BASE_SIZE6px;
+     border:RATE_BASE_SIZE1px solid rgba(218,224,230,1);
+}
+
+QComboBox QAbstractItemView:item
+{
+    outline:0px;
+    height: RATE_BASE_SIZE24px;
+    background:rgba(248,250,252,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(133,141,158,1);
+    border-radius:RATE_BASE_SIZE6px;
+}
+
+QComboBox QAbstractItemView:item:hover
+{
+    outline:0px;
+    background:rgba(248,250,252,1);
+    height:RATE_BASE_SIZE24px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(65,122,192,1);
+    border-radius:RATE_BASE_SIZE6px;
+    border:RATE_BASE_SIZE1px dashed rgba(76,157,255,1);
+}
+
+QPushButton#btn_downloadAnswerSheet,#btn_savePassword
+{
+    outline:0px;
+    background:rgba(19,187,138,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE10px;
+}
+
+
+
+QPushButton#btn_setRead,#btn_gobackList,#btn_expandPaper
+{
+    outline:0px;
+    background:rgba(254,119,100,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE10px;
+}
+
+QLineEdit#edt_newPassword,#edt_oldPassword,#edt_newPassword1,#edt_cloe_file
+{
+    background:rgba(239,240,245,1);
+    border:RATE_BASE_SIZE1px solid rgba(228,229,235,1);
+    border-radius:RATE_BASE_SIZE10px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+    padding-left:RATE_BASE_SIZE20px;
+}
+
+QWidget#widget_npw_BG,#widget_fc_BG,#widget_cloe_BG,#widget_fl_hint
+{
+
+    background:rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE16px;
+}
+
+QLabel#label_noticeIcon
+{
+    border-image:url(:/images/icon-notice-unread.png);
+}
+
+
+QLabel#label_noticeTitle
+{
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:700;
+    color:rgba(51,51,51,1);
+}
+
+QTextBrowser#txtb_noticeContent
+{
+    border:0px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QTextBrowser#txtb_pw_privacy
+{
+    border:0px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+    padding-left:RATE_BASE_SIZE40px;
+}
+
+
+QLabel#label_arrow
+{
+    border-image:url(:/images/icon-arrow.png);
+}
+
+QPushButton#btn_downloadApp
+{
+    outline:none;
+    background:rgba(244,243,247,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+    border-top-left-radius:RATE_BASE_SIZE6px;
+    border-bottom-left-radius:RATE_BASE_SIZE6px;
+}
+
+QPushButton#btn_bindUser
+{
+    outline:none;
+    background:rgba(19,187,138,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+    border-top-right-radius:RATE_BASE_SIZE6px;
+    border-bottom-right-radius:RATE_BASE_SIZE6px;
+}
+
+QLabel#label_useAppScan
+{
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QLabel#label_clsi_studentName
+{
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(34,34,34,1);
+}
+
+QLabel#label_clsi_studentCodeHint,#label_clsi_identityHint,#label_clsi_orgNameHint
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QLabel#label_clsi_studentCode,#label_clsi_identity,#label_clsi_orgName
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(51,51,51,1);
+}
+
+QPushButton#btn_clsi_editPassword
+{
+    outline:0px;
+    background:rgba(19,187,138,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE4px;
+}
+
+QPushButton#btn_clsi_exitLogin
+{
+    outline:0px;
+    background:rgba(254,119,100,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE4px;
+}
+
+QLabel#label_clnd_title
+{
+    font-size:RATE_BASE_SIZE24px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(51,51,51,1);
+}
+
+QLabel#label_clnd_time
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+}
+
+QWidget#widget_clop_top
+{
+    background:rgba(19,187,138,1);
+}
+
+QLabel#label_clop_avgCorrectRate,#label_clop_batch,#label_clop_course,#label_clop_maxCorrectRate,#label_clop_correctRateHint
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+}
+
+QLabel#label_wcw_icon
+{
+    border-image:url(:/images/icon-welcom.png);
+}
+
+QLabel#label_wcw_tips
+{
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QLabel#label_wcw_studentName,#label_wcw_specialty
+{
+    font-size:RATE_BASE_SIZE18px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51,51,51,1);
+}
+
+QWidget#widget_mask
+{
+    background:rgba(0,0,0,0.65);
+}
+
+QLabel#label_pw_title
+{
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51,51,51,1);
+}
+
+QPushButton#btn_pw_close,#btn_awmb_close
+{
+    border-image:url(:/images/btn-fc-close.png);
+}
+
+QPushButton#btn_pw_agree
+{
+    outline:none;
+    background:rgba(239,240,245,1);
+    border-radius:RATE_BASE_SIZE6px;
+
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+}
+
+QPushButton#btn_pw_disagree
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+    background:rgba(19,186,140,1);
+    border-radius:RATE_BASE_SIZE6px;
+}
+
+QPushButton#btn_clop_goback
+{
+    outline:none;
+    border:1px solid rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE10px;
+    background:transparent;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255, 255, 255,1);
+}
+
+QLabel#label_clop_reportTitle,#label_clop_correctRate
+{
+    font-size:RATE_BASE_SIZE20px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QLabel#label_clop_tips_title
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+}
+
+QLabel#label_clop_tips
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QLabel#label_wec_content
+{
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QLabel#label_wec_tips
+{
+    font-size:RATE_BASE_SIZE18px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(51,51,51,1);
+}
+
+QWidget#widget_et_steps
+{
+    background:rgba(239,240,245,1);
+}
+
+QPushButton#btn_previousStep
+{
+    outline:none;
+    border:0px solid rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE6px;
+    background:rgba(19,186,140,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255, 255, 255,1);
+}
+
+QPushButton#btn_nextStep
+{
+    outline:none;
+    border:0px solid rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE6px;
+    background:rgba(11,166,124,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255, 255, 255,1);
+}
+
+QLabel#label_et_networkIcon,#label_et_timeIcon,#label_et_cameraIcon,#label_et_voiceIcon,#label_et_mobileIcon,#label_et_resultIcon
+{
+    border:1px solid rgba(105,105,105,1);
+    border-radius:RATE_BASE_SIZE12px;
+    background:rgba(244,243,247,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(102, 102, 102,1);
+}
+
+QLabel#label_et_network,#label_et_time,#label_et_camera,#label_et_voice,#label_et_mobile,#label_et_result
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(113, 113, 113,1);
+}
+
+
+QLabel#label_et_HLine1,#label_et_HLine2,#label_et_HLine3,#label_et_HLine4,#label_et_HLine5
+{
+    background:rgba(218,217,229,1);
+}
+
+QToolButton#btn_IsSelf
+{
+    outline:none;
+    border:0px solid rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE16px;
+    background:rgba(19,186,140,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255, 255, 255,1);
+    padding-top:RATE_BASE_SIZE40px;
+    padding-bottom:RATE_BASE_SIZE40px;
+}
+
+QToolButton#btn_IsNotSelf
+{
+    outline:none;
+    border:0px solid rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE16px;
+    background:rgba(254,119,100,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255, 255, 255,1);
+    padding-top:RATE_BASE_SIZE40px;
+    padding-bottom:RATE_BASE_SIZE40px;
+}
+
+QWidget#widget_etvt_voice
+{
+    background:rgba(239,240,245,1);
+    border-radius:RATE_BASE_SIZE10px;
+}
+
+QPushButton#btn_etvt_play
+{
+    border-image:url(:/images/btn-etvt-play.png);
+}
+
+QPushButton#btn_etvt_volume
+{
+    border-image:url(:/images/btn-etvt-volume.png);
+}
+
+QPushButton#btn_etvt_download
+{
+    border-image:url(:/images/btn-etvt-download.png);
+}
+
+QToolButton#btn_etvt_canPlay
+{
+    outline:none;
+    border:0px solid rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE10px;
+    background:rgba(19,186,140,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255, 255, 255,1);
+    padding-left:RATE_BASE_SIZE20px;
+    padding-right:RATE_BASE_SIZE20px;
+}
+
+QToolButton#btn_etvt_canotPlay
+{
+    outline:none;
+    border:0px solid rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE10px;
+    background:rgba(254,119,100,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255, 255, 255,1);
+    padding-left:RATE_BASE_SIZE20px;
+    padding-right:RATE_BASE_SIZE20px;
+}
+
+QSlider#hslider_etvt_voice::groove:horizontal
+{
+    height:RATE_BASE_SIZE2px;
+    background:rgba(102,102,102,1);
+    border-radius:RATE_BASE_SIZE1px;
+}
+
+QSlider#hslider_etvt_voice::handle:horizontal
+{
+    background:rgba(19,187,138,1);
+    border-radius:RATE_BASE_SIZE6px;
+    width:RATE_BASE_SIZE12px;
+    height:RATE_BASE_SIZE12px;
+    margin-top:-RATE_BASE_SIZE5px;
+    margin-bottom:-RATE_BASE_SIZE5px;
+    margin-left:0px;
+}
+
+QSlider#hslider_etvt_voice::sub-page:horizontal
+{
+    border-radius:RATE_BASE_SIZE1px;
+    height:RATE_BASE_SIZE2px;
+    background:rgba(19,187,138,1);
+}
+
+QLabel#label_etmt_status
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(51,51,51,1);
+}
+
+QToolButton#btn_etmt_canotScan
+{
+    outline:none;
+    border:0px solid rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE10px;
+    background:rgba(254,119,100,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255, 255, 255,1);
+    padding-left:RATE_BASE_SIZE52px;
+}
+
+QToolButton#btn_etmt_uploadFaild
+{
+    outline:none;
+    border:0px solid rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE10px;
+    background:rgba(254,119,100,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255, 255, 255,1);
+    padding-left:RATE_BASE_SIZE76px;
+}
+
+QLabel#label_fc_title
+{
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51, 51, 51,1);
+}
+
+QLabel#label_fc_tips
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153, 153, 153,1);
+}
+
+QLabel#label_fc_note
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102, 102, 102,1);
+}
+
+QPushButton#btn_fc_compare
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(255,255,255,1);
+    background:rgba(254,119,100,1);
+    border-radius:RATE_BASE_SIZE10px;
+}
+
+QPushButton#btn_fc_close
+{
+    border-image:url(:/images/btn-fc-close.png);
+}
+
+QLabel#label_fc_basePhotoTips
+{
+    background:rgba(0,0,0,0.3);
+    border-radius:RATE_BASE_SIZE2px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+}
+
+QLabel#label_cloe_selFileType
+{
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51, 51, 51,1);
+}
+
+QRadioButton
+{
+    outline: none;
+    height:RATE_BASE_SIZE26px;
+    spacing: RATE_BASE_SIZE8px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QRadioButton::indicator
+{
+    width:RATE_BASE_SIZE16px;
+    height:RATE_BASE_SIZE16px;
+}
+
+QRadioButton::indicator:unchecked
+{
+    border-image: url(:/images/radiobtn-normal.png);
+}
+
+QRadioButton::indicator:checked
+{
+    border-image: url(:/images/radiobtn-check.png);
+}
+
+QPushButton#btn_cloe_selFile,#btn_comfirmUpload
+{
+    outline:none;
+    background:rgba(19,187,138,1);
+    border-radius:RATE_BASE_SIZE10px;
+
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+}
+
+QPushButton#btn_cancelUpload
+{
+    outline:none;
+    background:rgba(254,119,100,1);
+    border-radius:RATE_BASE_SIZE10px;
+
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+}
+
+QWidget#widget_objevtiveScore
+{
+    background:rgba(239,240,245,1);
+    border-radius:RATE_BASE_SIZE10px;
+    border:RATE_BASE_SIZE1px solid rgba(0,0,0,0.1);
+}
+
+QListWidget#lw_objectiveScore
+{
+    outline:none;
+    border:0px solid gray;
+    background:rgba(239,240,245,1);
+}
+
+
+QListWidget#lw_objectiveScore::item
+{
+    background:rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE10px;
+    border:0px;
+    margin-top:RATE_BASE_SIZE10px;
+    min-height:RATE_BASE_SIZE48px;
+}
+
+QWidget#widget_awbg_BG
+{
+    background:rgba(239, 240, 245,1);
+    border:RATE_BASE_SIZE1px solid rgba(212,212,214,1);
+}
+
+QWidget#widget_awbg_top
+{
+    background:rgba(19, 187, 138,1);
+}
+
+QLabel#label_awbg_examIcon
+{
+    border-image:url(:/images/icon-awbg-exam.png);
+}
+
+QLabel#label_awbg_exam,#label_awbg_student
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QLabel#label_awbg_studentIcon
+{
+    border-image:url(:/images/icon-awbg-student.png);
+}
+
+
+QWidget#widget_we_preNotice,#widget_we_paperStruct,#widget_aw_answerBG,#widget_navigate,#widget_awes_BG
+{
+    background:rgba(255, 255, 255,1);
+    border-radius:RATE_BASE_SIZE10px;
+}
+
+QLabel#label_we_struct
+{
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(68,68,68,1);
+}
+
+QLabel#label_we_totalScore
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(153,153,153,1);
+}
+
+QLabel#label_we_notice
+{
+    font-size:RATE_BASE_SIZE20px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(34,34,34,1);
+}
+
+QListWidget#lw_we_struct
+{
+    outline:none;
+    border:0px solid gray;
+    background:rgba(255,255,255,1);
+}
+
+
+QListWidget#lw_we_struct::item
+{
+    background:rgba(255,255,255,1);
+    border:0px;
+    border-top:RATE_BASE_SIZE1px solid rgba(240,244,249,1);
+}
+
+QPushButton#btn_we_startExam
+{
+    background:rgba(254,119,100,1);
+    border-radius:RATE_BASE_SIZE10px;
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,0.5);
+}
+
+
+QTextBrowser#txtb_we_prenotice
+{
+    border:0px;
+}
+
+QWidget#widget_aw_top,#widget_ng_top
+{
+    background:rgba(239,240,245,1);
+    border-top-left-radius:RATE_BASE_SIZE10px;
+    border-top-right-radius:RATE_BASE_SIZE10px;
+}
+
+QLabel#label_answerProgress
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(153,153,153,1);
+}
+
+QProgressBar#pgb_answerProgress
+{
+    border:0px;
+    border-radius:RATE_BASE_SIZE2px;
+    background:rgba(255,255,255,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+    text-align:right;
+    margin-right:RATE_BASE_SIZE35px;
+    margin-top:RATE_BASE_SIZE6px;
+    margin-bottom:RATE_BASE_SIZE6px;
+}
+
+QProgressBar#pgb_answerProgress::chunk
+{
+    border:0px;
+    border-radius:RATE_BASE_SIZE2px;
+    background:rgba(19,187,138,1);
+}
+
+QPushButton#btn_handInPaper
+{
+    background:rgba(254,119,100,1);
+    border-radius:RATE_BASE_SIZE10px;
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+
+QLabel#label_lastTimeIcon
+{
+    border-image:url(:/images/icon-last-time.png);
+}
+
+QLabel#label_lastTime
+{
+    font-size:RATE_BASE_SIZE24px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51,51,51,1);
+}
+
+QPushButton#btn_all,#btn_answered,#btn_unanswered,#btn_marked
+{
+    outline:none;
+    background:rgba(242,243,247,1);
+    border-radius:RATE_BASE_SIZE14px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(153,153,153,1);
+    padding-left:RATE_BASE_SIZE15px;
+    text-align:left;
+}
+
+
+QPushButton#btn_allNum
+{
+    outline:none;
+    background:rgba(153,153,153,1);
+    border-radius:RATE_BASE_SIZE10px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QPushButton#btn_answeredNum
+{
+    outline:none;
+    background:rgba(131,172,247,1);
+    border-radius:RATE_BASE_SIZE10px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QPushButton#btn_unansweredNum
+{
+    outline:none;
+    background:rgba(254,119,100,1);
+    border-radius:RATE_BASE_SIZE10px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QPushButton#btn_markedNum
+{
+    outline:none;
+    background:rgba(255,159,67,1);
+    border-radius:RATE_BASE_SIZE10px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QLabel#label_awhp_icon
+{
+    border-image:url(:/images/icon-handin-paper.png);
+}
+
+QLabel#label_awhp_title
+{
+    font-size:RATE_BASE_SIZE18px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51,51,51,1);
+}
+
+
+QLabel#label_awhp_answered,#label_awhp_unanswered,#label_awhp_marked
+{
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QPushButton#btn_awhp_comfirm
+{
+    outline:none;
+    background:rgba(19,186,140,1);
+    border-radius:RATE_BASE_SIZE6px;
+
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+}
+
+QPushButton#btn_awhp_cancel
+{
+    outline:none;
+    background:rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE6px;
+
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+}
+
+QLabel#label_awhp_title
+{
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51,51,51,1);
+}
+
+QLabel#label_awes_title
+{
+    font-size:RATE_BASE_SIZE20px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51,51,51,1);
+}
+
+
+QLabel#label_awes_result
+{
+    font-size:RATE_BASE_SIZE24px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QTextBrowser#txtb_awes_postNotice,#txtb_awes_breachNotice
+{
+    border:0px;
+    border-radius:RATE_BASE_SIZE10px;
+    background:rgba(239,240,245,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QPushButton#btn_awes_goback
+{
+    outline:none;
+    background:rgba(19,187,138,1);
+    border-radius:RATE_BASE_SIZE10px;
+
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QPushButton#btn_qaab_mark,#btn_qaab_cut,#btn_qaab_paste,#btn_qaab_superscript,#btn_qaab_subscript,#btn_qaab_cancelSub,#btn_qaab_cancelSup
+{
+    outline:none;
+    background:rgba(19,187,138,1);
+    border-radius:RATE_BASE_SIZE4px;
+
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+}
+
+QLabel#label_awmb_content
+{
+    font-size:RATE_BASE_SIZE20px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(51,51,51,1);
+}
+
+QTextEdit#tedt_blank,#tedt_qaa_blank
+{
+    background:rgba(239,240,245,1);
+    border-radius:RATE_BASE_SIZE10px;
+    border:RATE_BASE_SIZE1px solid rgba(228,229,235,1);
+
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+
+QTextBrowser#tb_questionBody
+{
+    border:0px;
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(51,51,51,1);
+}
+
+
+QPushButton#btn_mark
+{
+    outline:none;
+    border-image:url(:/images/btn-mark.png);
+}
+
+QPushButton#btn_ap_play
+{
+    border-image:url(:/images/btn-audio-play.png);
+}
+
+QLabel#label_ap_time
+{
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(252,130,114,1);
+}
+
+
+QScrollBar:vertical
+{
+    border:0px;
+    background:rgba(255,255,255,1);
+    width:RATE_BASE_SIZE8px;
+    margin:0 0 0 0px;
+}
+
+
+QScrollBar::handle:vertical
+{
+    background:rgba(227,228,232,1);
+    border-radius:RATE_BASE_SIZE4px;
+}
+
+QScrollBar::add-line:vertical
+{
+    border:0px solid rgba(227,228,232,1);
+    background:rgba(255,255,255,1);
+}
+
+
+QScrollBar::sub-line:vertical
+{
+    border:0px solid rgba(255,255,255,1);
+    background:rgba(255,255,255,1);
+}
+
+
+QScrollBar::up-arrow:vertical,QScrollBar::down-arrow:vertical
+{
+    border:0px solid rgba(255,255,255,1);
+    width:0px;
+    height:0px;
+    background:rgba(255,255,255,1);
+}
+
+
+QScrollBar::add-page:vertical
+{
+    background:rgba(255,255,255,1);
+}
+
+QScrollBar::sub-page:vertical
+{
+    background:rgba(255,255,255,1);
+}
+
+
+QListWidget#lw_aw_questionList
+{
+    outline:none;
+    border:0px solid gray;
+    background:rgba(255,255,255,1);
+    padding-left:RATE_BASE_SIZE30px;
+}
+
+
+QListWidget#lw_aw_questionList::item
+{
+    background:rgba(255,255,255,1);
+    border:0px;
+}
+
+QLabel#label_questionName
+{
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51,51,51,1);
+}
+
+QLabel#label_questionScore
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+}
+
+QPushButton#btn_awqn_quesion
+{
+    outline:none;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(136,146,146,1);
+    background:rgba(239,240,245,1);
+    border:0px;
+    border-radius:RATE_BASE_SIZE15px;
+}
+
+QLabel#label_bigQuesionName
+{
+    font-size:RATE_BASE_SIZE18px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(34,34,34,1);
+}
+
+QTextBrowser#tb_questionBody
+{
+    border:none;
+    background:rgba(255,255,255,1);
+    padding:0 0 0 0px;
+
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(51,51,51,1);
+}
+
+QTextBrowser#tb_option
+{
+    outline:none;
+    border:none;
+    background:rgba(255,255,255,1);
+    padding:0 0 0 0px;
+
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(102,102,102,1);
+}
+
+
+
+QTextEdit#tedt_qaa_blank
+{
+    outline:none;
+    background:rgba(239,240,245,1);
+    border-radius:RATE_BASE_SIZE10px;
+    border:1px solid rgba(228,229,235,1);
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(51,51,51,1);
+    padding:20 20 20 20px;
+}
+
+QTextEdit#tedt_blank
+{
+    outline:none;
+    background:rgba(239,240,245,1);
+    border-radius:RATE_BASE_SIZE10px;
+    border:1px solid rgba(228,229,235,1);
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:500;
+    color:rgba(51,51,51,1);
+    padding-left:20px;
+    padding-right:20px;
+    padding-top:4px;
+    padding-bottom:4px;
+}
+
+QListWidget#list_choiceQuestion,#list_question,#list_body,#list_options,#listWidget_mobileMonitor,#lw_reportInfo,#lw_paperDetail
+{
+    outline:none;
+    border-radius:0px;
+    border:0px solid rgba(235,235,235,1);
+    background:rgba(255,255,255,1);
+}
+
+QListWidget#list_choiceQuestion::item,#list_question::item,#list_body::item,#listWidget_mobileMonitor::item,#lw_reportInfo::item,#lw_paperDetail::item
+{
+    outline:none;
+    border:none;
+    background:rgba(255,255,255,1);
+}
+
+QListWidget#list_choiceQuestion::item:hover,#list_question::item:hover,#list_body::item:hover,
+#list_options::item:hover,#listWidget_mobileMonitor::item:hover,#lw_reportInfo::item:hover,#lw_paperDetail::item:hover
+{
+    outline:none;
+    border:none;
+    background:rgba(255,255,255,1);
+}
+
+QListWidget#list_choiceQuestion::item:selected,#list_question::item:selected,#list_body::item:selected,
+#list_options::item:selected,#listWidget_mobileMonitor::item:selected,#lw_reportInfo::item:selected,#lw_paperDetail::item:selected
+{
+    outline:none;
+    border:none;
+    background:rgba(255,255,255,1);
+}
+
+QPushButton#btn_cq_previous,#btn_cq_next
+{
+    outline:none;
+    background:rgba(19,187,138,1);
+    border-radius:RATE_BASE_SIZE10px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QPushButton#btn_blankName
+{
+    outline:none;
+    background:rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE15px;
+    border:1px solid rgba(189,200,217,1);
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(239,240,245,1);
+}
+
+QWidget#widget_audioPlay
+{
+    border-radius:RATE_BASE_SIZE20px;
+    background:rgba(239,240,245,1);
+}
+
+QWidget#widget_cloe_viewpaper
+{
+    border-radius:RATE_BASE_SIZE10px;
+    background:rgba(255,255,255,1);
+}
+
+QLabel#label_clopvp_paperName
+{
+    font-size:RATE_BASE_SIZE28px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51,51,51,1);
+}
+
+QLabel#label_clopvp_courseInfo
+{
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QTextBrowser#tb_clopvp_paper
+{
+    border:0px;
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(51,51,51,1);
+}
+
+QPushButton#btn_sa_showAnswer
+{
+    outline:none;
+    border:0px;
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(19,187,138,1);
+    background:rgba(255,255,255,1);
+}
+
+QLabel#label_sa_arrow
+{
+    border-image:url(:/images/icon-show-answer.png);
+}
+
+QLabel#label_sa_answer
+{
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QTextBrowser#txtb_sa_answer
+{
+    border:0px;
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QListWidget#lw_ettr_unpass
+{
+    outline:none;
+    border:0px solid gray;
+    background:rgba(255,255,255,1);
+}
+
+
+QListWidget#lw_ettr_unpass::item
+{
+    background:rgba(255,255,255,1);
+    border-radius:0px;
+    border:0px;
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(254,119,100,1);
+}
+
+QListWidget#lw_ettr_unpass::item:selected
+{
+    outline:none;
+    border:none;
+    background:rgba(255,255,255,1);
+}
+
+QLabel#label_wxa_statusIcon
+{
+    border-image:url(:/images/icon-env-test-pass.png);
+}
+
+QLabel#label_wxa_status
+{
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(19,186,140,1);
+}
+
+QLabel#label_wxa_title
+{
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(51,51,51,1);
+}
+
+
+QLabel#label_wxa_answerTips
+{
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QPushButton#btn_wxa_comfirm
+{
+    outline:none;
+    border-image:url(:/images/btn-wxa-comfirm.png);
+
+    font-size:RATE_BASE_SIZE14px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(255,255,255,1);
+}
+
+
+QPushButton#btn_wxa_close
+{
+    outline:none;
+    border-image:url(:/images/btn-wxa-close.png);
+}
+
+QPushButton#btn_addPic
+{
+    outline:none;
+    border-image:url(:/images/btn-add-pic.png);
+}
+
+QLabel#label_ap_tips
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(153,153,153,1);
+}
+
+QWidget#widget_operation
+{
+    background:rgba(89,89,89,0.8);
+}
+
+
+QPushButton#btn_delete
+{
+    outline:none;
+    border-image:url(:/images/icon-del-pic.png);
+}
+
+QPushButton#btn_view
+{
+    outline:none;
+    border-image:url(:/images/icon-view-pic.png);
+}
+
+
+QPushButton#btn_prev_photo
+{
+    border-image:url(:/images/btn-prev-photo.png);
+}
+
+QPushButton#btn_next_photo
+{
+    border-image:url(:/images/btn-next-photo.png);
+}
+
+QPushButton#btn_vp_close
+{
+    border-image:url(:/images/btn-fc-close.png);
+}
+
+QWidget#widget_vp_client
+{
+    background:rgba(255,255,255,1);
+    border-radius:RATE_BASE_SIZE10px;
+    border:6px solid rgba(0,0,0,0.1);
+}
+
+QWidget#widget_vp_bg
+{
+    background:rgba(32,43,75,0.7);
+    border-radius:0px;
+    border:0px solid rgba(0,0,0,0.1);
+}
+
+QLabel#label_fl_title
+{
+    font-size:RATE_BASE_SIZE18px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51,51,51,1);
+}
+
+
+QLabel#label_fl_time
+{
+    font-size:RATE_BASE_SIZE20px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(252,130,114,1);
+}
+
+QLabel#label_fl_tips
+{
+    font-size:RATE_BASE_SIZE12px;
+    font-family:"Microsoft YaHei";
+    font-weight:400;
+    color:rgba(102,102,102,1);
+}
+
+QLabel#label_fl_actionTips
+{
+    font-size:RATE_BASE_SIZE18px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QWidget#widget_fl_action
+{
+    background:rgba(229,107,90,1);
+    margin-top:RATE_BASE_SIZE8px;
+    margin-bottom:RATE_BASE_SIZE8px;
+    border-radius:RATE_BASE_SIZE20px;
+
+}
+
+QPushButton#btn_fl_time
+{
+    outline:none;
+    border-radius:RATE_BASE_SIZE20px;
+    border:RATE_BASE_SIZE2px solid rgba(255,255,255,1);
+    background:rgba(229,107,90,1);
+
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QPushButton#btn_fl_startVerify
+{
+    outline:none;
+    border-radius:RATE_BASE_SIZE10px;
+    border:0px solid rgba(255,255,255,1);
+    background:rgba(19,187,138,1);
+
+    font-size:RATE_BASE_SIZE16px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(255,255,255,1);
+}
+
+QLabel#label_fl_hint
+{
+    font-size:RATE_BASE_SIZE24px;
+    font-family:"Microsoft YaHei";
+    font-weight:600;
+    color:rgba(51,51,51,1);
+}

+ 227 - 0
client/cloeUploadFile.cpp

@@ -0,0 +1,227 @@
+#include "cloeUploadFile.h"
+#include "ui_cloeUploadFile.h"
+
+#include "CAppInfo.h"
+#include "awMsgBox.h"
+#include "CCommonTools.h"
+#include <QDesktopWidget>
+#include <QFileDialog>
+
+
+cloeUploadFile::cloeUploadFile(__int64 nExamRecordDataId, std::vector<QString> vFileTypes, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::cloeUploadFile), m_nExamRecordDataId(nExamRecordDataId)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+    
+    ui->rdoBtn_zip->setVisible(false);
+    ui->rdoBtn_pdf->setVisible(false);
+    ui->rdoBtn_picture->setVisible(false);
+    for(QString sType : vFileTypes)
+    {
+        if(sType == "ZIP")
+        {
+            ui->rdoBtn_zip->setVisible(true);
+        }
+
+        if(sType == "PDF")
+        {
+            ui->rdoBtn_pdf->setVisible(true);
+        }
+
+        if(sType == "IMAGE")
+        {
+            ui->rdoBtn_picture->setVisible(true);
+        }
+    }
+
+	initUI();
+
+	qRegisterMetaType<CSubmitOfflinePaper>("CSubmitOfflinePaper");
+    connect(g_httpBllPtr.get(), &CHttpBll::sgnSubmitOfflinePaper, this, &cloeUploadFile::onSubmitOfflinePaper);
+}
+
+cloeUploadFile::~cloeUploadFile()
+{
+    delete ui;
+}
+
+void cloeUploadFile::initUI()
+{
+    QDesktopWidget *dekwiget = QApplication::desktop();
+    setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+    ui->widget_mask->setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+    ui->widget_cloe_BG->setGeometry((width() - g_appInfoPtr->m_fRate*490)/2, (height() - g_appInfoPtr->m_fRate*295)/2,
+                                   g_appInfoPtr->m_fRate*490, g_appInfoPtr->m_fRate*295);
+    ui->label_fc_title->adjustSize();
+    ui->label_fc_title->setGeometry(g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*16,
+                                    ui->label_fc_title->width(), ui->label_fc_title->height());
+    ui->btn_fc_close->setGeometry(ui->widget_cloe_BG->width() - g_appInfoPtr->m_fRate*(20 + 16),
+                                  g_appInfoPtr->m_fRate*16, g_appInfoPtr->m_fRate*16, g_appInfoPtr->m_fRate*16);
+    ui->label_HLine->setGeometry(0, ui->label_fc_title->y() + ui->label_fc_title->height() + g_appInfoPtr->m_fRate*16,
+                                 ui->widget_cloe_BG->width(), g_appInfoPtr->m_fRate*1);
+    ui->label_cloe_selFileType->adjustSize();
+    ui->label_cloe_selFileType->setGeometry(g_appInfoPtr->m_fRate*30, ui->label_HLine->y() + ui->label_HLine->height() + g_appInfoPtr->m_fRate*30,
+                                            ui->label_cloe_selFileType->width(), ui->label_cloe_selFileType->height());
+    int nLeftRadio = ui->label_cloe_selFileType->x() + ui->label_cloe_selFileType->width() + g_appInfoPtr->m_fRate*26;
+    ui->rdoBtn_zip->adjustSize();
+    ui->rdoBtn_zip->setGeometry(nLeftRadio,
+                                ui->label_cloe_selFileType->y() + (ui->label_cloe_selFileType->height() - ui->rdoBtn_zip->height())/2,
+                                ui->rdoBtn_zip->width(), ui->rdoBtn_zip->height());
+    nLeftRadio = ui->rdoBtn_zip->x() + ui->rdoBtn_zip->width() + g_appInfoPtr->m_fRate*20;
+
+    ui->rdoBtn_pdf->adjustSize();
+    ui->rdoBtn_pdf->setGeometry(nLeftRadio,
+                                ui->rdoBtn_zip->y(), ui->rdoBtn_pdf->width(), ui->rdoBtn_pdf->height());
+    nLeftRadio = ui->rdoBtn_pdf->x() + ui->rdoBtn_pdf->width() + g_appInfoPtr->m_fRate*30;    
+    
+    ui->rdoBtn_picture->adjustSize();
+    ui->rdoBtn_picture->setGeometry(nLeftRadio,
+                                    ui->rdoBtn_zip->y(), ui->rdoBtn_picture->width(), ui->rdoBtn_picture->height());
+
+    ui->edt_cloe_file->setGeometry(ui->label_cloe_selFileType->x(), ui->label_cloe_selFileType->y() + ui->label_cloe_selFileType->height() + g_appInfoPtr->m_fRate*20,
+                                   g_appInfoPtr->m_fRate*360, g_appInfoPtr->m_fRate*40);
+    ui->btn_cloe_selFile->setGeometry(ui->edt_cloe_file->x() + ui->edt_cloe_file->width() + g_appInfoPtr->m_fRate*10,
+                                      ui->edt_cloe_file->y() + (ui->edt_cloe_file->height() - g_appInfoPtr->m_fRate*36)/2,
+                                      g_appInfoPtr->m_fRate*60, g_appInfoPtr->m_fRate*36);
+    ui->btn_comfirmUpload->setGeometry(ui->widget_cloe_BG->width() - g_appInfoPtr->m_fRate*(30+120), ui->widget_cloe_BG->height() - g_appInfoPtr->m_fRate*(30+40),
+                                       g_appInfoPtr->m_fRate*120, g_appInfoPtr->m_fRate*40);
+    ui->btn_cancelUpload->setGeometry(ui->btn_comfirmUpload->x() - g_appInfoPtr->m_fRate*10 - ui->btn_comfirmUpload->width(),
+                                      ui->btn_comfirmUpload->y(), ui->btn_comfirmUpload->width(), ui->btn_comfirmUpload->height());
+}
+
+void cloeUploadFile::showEvent(QShowEvent *)
+{
+	int nLeftRadio = ui->label_cloe_selFileType->x() + ui->label_cloe_selFileType->width() + g_appInfoPtr->m_fRate * 26;	
+	if (ui->rdoBtn_zip->isVisible())
+	{
+		nLeftRadio = ui->rdoBtn_zip->x() + ui->rdoBtn_zip->width() + g_appInfoPtr->m_fRate * 20;
+	}
+
+	if (ui->rdoBtn_pdf->isVisible())
+	{	
+		ui->rdoBtn_pdf->setGeometry(nLeftRadio,
+			ui->rdoBtn_zip->y(), ui->rdoBtn_pdf->width(), ui->rdoBtn_pdf->height());
+		nLeftRadio = ui->rdoBtn_pdf->x() + ui->rdoBtn_pdf->width() + g_appInfoPtr->m_fRate * 30;
+	}
+
+	if (ui->rdoBtn_picture->isVisible())
+	{	
+		ui->rdoBtn_picture->setGeometry(nLeftRadio,
+			ui->rdoBtn_zip->y(), ui->rdoBtn_picture->width(), ui->rdoBtn_picture->height());
+	}
+}
+
+void cloeUploadFile::on_btn_cloe_selFile_clicked()
+{
+    QString sTitle = "";
+    QString sExt = "";
+    if(ui->rdoBtn_zip->isChecked())
+    {
+        sTitle = QString::fromLocal8Bit("选择ZIP文件");
+        sExt = QString::fromLocal8Bit("WinRAR ZIP 压缩文件(*.zip)");
+    }
+    else if(ui->rdoBtn_pdf->isChecked())
+    {
+        sTitle = QString::fromLocal8Bit("选择PDF文件");
+        sExt = QString::fromLocal8Bit("PDF文件(*.pdf)");
+    }
+    else if(ui->rdoBtn_picture->isChecked())
+    {
+        sTitle = QString::fromLocal8Bit("选择图片文件");
+        sExt = QString::fromLocal8Bit("图片文件(*.jpeg;*.jpg;*.png)");
+    }
+	else
+	{		
+		ShowMsg(QString::fromLocal8Bit("请先选择文件类型"), this);
+		return;
+	}
+	
+	if (ui->rdoBtn_picture->isChecked())
+	{
+		QStringList fileNames = QFileDialog::getOpenFileNames(this,
+			sTitle,
+			".",
+			sExt);
+		ui->edt_cloe_file->setText(fileNames.join(";"));
+	}
+	else
+	{ 
+		QString fileName = QFileDialog::getOpenFileName(this,
+			sTitle,
+			".",
+			sExt);
+		ui->edt_cloe_file->setText(fileName);
+	}
+    
+}
+
+void cloeUploadFile::on_btn_cancelUpload_clicked()
+{
+    emit cancel();
+}
+
+void cloeUploadFile::on_btn_comfirmUpload_clicked()
+{
+	if (ui->edt_cloe_file->text().isEmpty())
+	{
+		ShowMsg(QString::fromLocal8Bit("请先选择文件,再上传"), this);
+		return;
+	}
+	
+	QString sType = "";
+	if (ui->rdoBtn_zip->isChecked())
+	{
+		sType = "ZIP";		
+	}
+	else if (ui->rdoBtn_pdf->isChecked())
+	{
+		sType = "PDF";
+	}
+	else if (ui->rdoBtn_picture->isChecked())
+	{
+		sType = "IMAGE";
+	}
+	QString sFileNames = ui->edt_cloe_file->text();
+	QStringList sFileList = sFileNames.split(";");
+	CHttpRequestPackage hrp;
+	hrp.sUri = "/api/ecs_oe_admin/client/exam/process/batchSubmitPaper";
+	hrp.nRequestType = RequestType::rtSubmitOfflinePaper;
+	hrp.sParamList.push_back(QString("examRecordDataId,%1").arg(m_nExamRecordDataId));
+	for (QString sFile : sFileList)
+	{
+        hrp.sParamList.push_back(QString("formdataFileType,fileArray,%1").arg(sFile));
+		hrp.sParamList.push_back(QString("fileMd5Array,%1").arg(CCommonTools::fileMd5(sFile)));
+	}
+	
+	hrp.sParamList.push_back(QString("fileType,%1").arg(sType));
+	hrp.eParamType = HttpParamType::hptFormdata;
+	g_httpBllPtr->post(hrp);
+}
+
+void cloeUploadFile::onSubmitOfflinePaper(CSubmitOfflinePaper submitOfflinePaper)
+{
+	if (submitOfflinePaper.nCode == 200)
+	{
+		ShowMsg(QString::fromLocal8Bit("上传答案完成"), this);
+		emit uploadSucceed();
+	}
+	else
+	{
+        if(submitOfflinePaper.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("上传答案失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(submitOfflinePaper.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void cloeUploadFile::on_btn_fc_close_clicked()
+{
+    emit cancel();
+}

+ 38 - 0
client/cloeUploadFile.h

@@ -0,0 +1,38 @@
+#ifndef CLOEUPLOADFILE_H
+#define CLOEUPLOADFILE_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+
+namespace Ui {
+class cloeUploadFile;
+}
+
+class cloeUploadFile : public QWidget
+{
+    Q_OBJECT
+signals:
+    void cancel();
+    void uploadSucceed();
+public:
+    explicit cloeUploadFile(__int64 nExamRecordDataId, std::vector<QString> vFileTypes, QWidget *parent = nullptr);
+    ~cloeUploadFile();
+
+    void initUI();
+private slots:
+    void on_btn_cloe_selFile_clicked();
+
+    void on_btn_cancelUpload_clicked();
+
+    void on_btn_comfirmUpload_clicked();
+
+    void on_btn_fc_close_clicked();
+
+	void onSubmitOfflinePaper(CSubmitOfflinePaper submitOfflinePaper);
+private:
+	void showEvent(QShowEvent *);
+    Ui::cloeUploadFile *ui;
+	__int64 m_nExamRecordDataId;
+};
+
+#endif // CLOEUPLOADFILE_H

+ 182 - 0
client/cloeUploadFile.ui

@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>cloeUploadFile</class>
+ <widget class="QWidget" name="cloeUploadFile">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>619</width>
+    <height>467</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_mask" native="true">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>20</y>
+     <width>521</width>
+     <height>411</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_cloe_BG" native="true">
+    <property name="geometry">
+     <rect>
+      <x>40</x>
+      <y>30</y>
+      <width>411</width>
+      <height>301</height>
+     </rect>
+    </property>
+    <widget class="QLabel" name="label_fc_title">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>20</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>上传文件</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_fc_close">
+     <property name="geometry">
+      <rect>
+       <x>333</x>
+       <y>10</y>
+       <width>31</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_HLine">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>60</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_cloe_selFileType">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>100</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>请选择上传文件类型:</string>
+     </property>
+    </widget>
+    <widget class="QRadioButton" name="rdoBtn_zip">
+     <property name="geometry">
+      <rect>
+       <x>90</x>
+       <y>100</y>
+       <width>89</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>ZIP</string>
+     </property>
+    </widget>
+    <widget class="QRadioButton" name="rdoBtn_pdf">
+     <property name="geometry">
+      <rect>
+       <x>190</x>
+       <y>100</y>
+       <width>89</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>PDF</string>
+     </property>
+    </widget>
+    <widget class="QRadioButton" name="rdoBtn_picture">
+     <property name="geometry">
+      <rect>
+       <x>290</x>
+       <y>100</y>
+       <width>89</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>图片</string>
+     </property>
+    </widget>
+    <widget class="QLineEdit" name="edt_cloe_file">
+     <property name="geometry">
+      <rect>
+       <x>80</x>
+       <y>140</y>
+       <width>181</width>
+       <height>20</height>
+      </rect>
+     </property>
+     <property name="placeholderText">
+      <string>请选择文件</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_cloe_selFile">
+     <property name="geometry">
+      <rect>
+       <x>294</x>
+       <y>140</y>
+       <width>41</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>选择</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_cancelUpload">
+     <property name="geometry">
+      <rect>
+       <x>80</x>
+       <y>210</y>
+       <width>75</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>取消上传</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_comfirmUpload">
+     <property name="geometry">
+      <rect>
+       <x>170</x>
+       <y>210</y>
+       <width>75</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>确认上传</string>
+     </property>
+    </widget>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 129 - 0
client/cloeViewPaper.cpp

@@ -0,0 +1,129 @@
+#include "cloeViewPaper.h"
+#include "ui_cloeViewPaper.h"
+
+#include "CAppInfo.h"
+#include "awMsgBox.h"
+#include "json/json.h"
+#include <QDesktopWidget>
+
+
+cloeViewPaper::cloeViewPaper(QString sPaper, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::cloeViewPaper)
+{
+    ui->setupUi(this);
+	
+	initPaper(sPaper);
+
+	initUI();		
+}
+
+cloeViewPaper::~cloeViewPaper()
+{
+    delete ui;
+}
+
+void cloeViewPaper::initUI()
+{     
+     QDesktopWidget *dekwiget = QApplication::desktop();
+     setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+     ui->widget_awbg_BG->setGeometry(0, 0, width(), height());
+     ui->widget_cloe_viewpaper->setGeometry(g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*56, width() - g_appInfoPtr->m_fRate*20*2,
+                                            height() - g_appInfoPtr->m_fRate*(56 + 38));     
+	 ui->label_clopvp_paperName->adjustSize();
+	 ui->label_clopvp_paperName->setGeometry((ui->widget_cloe_viewpaper->width() - ui->label_clopvp_paperName->width())/2,
+		 g_appInfoPtr->m_fRate * 30, ui->label_clopvp_paperName->width(), ui->label_clopvp_paperName->height());
+	 ui->label_clopvp_courseInfo->adjustSize();
+	 ui->label_clopvp_courseInfo->setGeometry((ui->widget_cloe_viewpaper->width() - ui->label_clopvp_courseInfo->width()) / 2,
+		 ui->label_clopvp_paperName->y() + ui->label_clopvp_paperName->height() + g_appInfoPtr->m_fRate * 10, 
+		 ui->label_clopvp_courseInfo->width(), ui->label_clopvp_courseInfo->height());
+	 ui->label_HLine->setGeometry(0, ui->label_clopvp_courseInfo->y() + ui->label_clopvp_courseInfo->height() + g_appInfoPtr->m_fRate * 30,
+		 ui->widget_cloe_viewpaper->width(), g_appInfoPtr->m_fRate *1);
+	 ui->tb_clopvp_paper->setGeometry(g_appInfoPtr->m_fRate * 40, ui->label_HLine->y() + ui->label_HLine->height() + g_appInfoPtr->m_fRate * 30,
+                 ui->widget_cloe_viewpaper->width() - g_appInfoPtr->m_fRate * 40*2, ui->widget_cloe_viewpaper->height() - ui->label_HLine->y() - ui->label_HLine->height() - g_appInfoPtr->m_fRate * 30*2);
+	 ui->widget_awbg_top->setGeometry(0, 0, ui->widget_awbg_BG->width(), g_appInfoPtr->m_fRate*112);
+     ui->label_awbg_examIcon->setGeometry(g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*20,
+                                          g_appInfoPtr->m_fRate*16, g_appInfoPtr->m_fRate*16);
+     ui->label_awbg_exam->adjustSize();
+     ui->label_awbg_exam->setGeometry(ui->label_awbg_examIcon->x() + ui->label_awbg_examIcon->width() + g_appInfoPtr->m_fRate*10,
+                                      ui->label_awbg_examIcon->y() + (ui->label_awbg_examIcon->height() - ui->label_awbg_exam->height())/2,
+                                      ui->label_awbg_exam->width(), ui->label_awbg_exam->height());
+     ui->btn_clop_goback->setGeometry(ui->widget_awbg_top->width() - g_appInfoPtr->m_fRate*(20 + 108), g_appInfoPtr->m_fRate*12,
+                                      g_appInfoPtr->m_fRate*108, g_appInfoPtr->m_fRate*32);
+     ui->btn_clop_goback->setIconSize(QSize(g_appInfoPtr->m_fRate*14, g_appInfoPtr->m_fRate*14));
+     ui->btn_clop_goback->setIcon(QIcon(":/images/icon-goback-list.png"));
+     ui->label_cl_company->adjustSize();
+     ui->label_cl_company->setGeometry(g_appInfoPtr->m_fRate*30, ui->widget_awbg_BG->height() - g_appInfoPtr->m_fRate*10 - ui->label_cl_company->height(),
+                                       ui->label_cl_company->width(), ui->label_cl_company->height());
+     ui->label_cl_version->adjustSize();
+     ui->label_cl_version->setGeometry(ui->widget_awbg_BG->width() - g_appInfoPtr->m_fRate*30 - ui->label_cl_version->width(),
+                                       ui->label_cl_company->y(), ui->label_cl_version->width(), ui->label_cl_version->height());
+     ui->label_serverTime->adjustSize();
+     ui->label_serverTime->setGeometry(ui->label_cl_version->x() - g_appInfoPtr->m_fRate*20 - ui->label_serverTime->width(),
+                                       ui->label_cl_company->y(), ui->label_serverTime->width(), ui->label_serverTime->height());
+
+}
+
+void cloeViewPaper::initPaper(QString sPaper)
+{
+	Json::Reader reader;
+	Json::Value jPaper = Json::Value::null;
+	if (!reader.parse(sPaper.toStdString(), jPaper))
+	{
+		ShowMsg(QString::fromLocal8Bit("解析试卷出错!"), this);		
+		return;
+	}
+
+	
+	ui->label_clopvp_paperName->setText(QString::fromLocal8Bit("%1 试卷").arg(jPaper["name"].asString().c_str()));
+	ui->label_clopvp_courseInfo->setText(QString::fromLocal8Bit("课程代码:%1  |  本试卷满分%2分")
+		.arg(jPaper["course"]["code"].asString().c_str()).arg(jPaper["totalScore"].asDouble()));
+
+	QTextCursor cursor = ui->tb_clopvp_paper->textCursor();
+	QTextBlockFormat textStyleFormat = cursor.blockFormat();
+	
+	textStyleFormat.setLineHeight(g_appInfoPtr->m_fRate * 5, QTextBlockFormat::LineDistanceHeight);
+	cursor.setBlockFormat(textStyleFormat);
+
+	int nIndex = 1;
+	int nSize = jPaper["paperDetails"].size();
+	for (int i = 0; i < nSize; ++i)
+	{
+		Json::Value jPaperDetails = jPaper["paperDetails"][i];
+		QString sTitle = QString::fromLocal8Bit("%1、%2(本大题共%3小题,满分%4分)")
+			.arg(jPaperDetails["cnNum"].asString().c_str()).arg(jPaperDetails["name"].asString().c_str())
+			.arg(jPaperDetails["unitCount"].asString().c_str()).arg(jPaperDetails["score"].asString().c_str());
+		QTextCharFormat fmt;
+		QFont font;
+		font.setFamily("Microsoft YaHei");
+		font.setPixelSize(g_appInfoPtr->m_fRate * 16);
+		font.setWeight(QFont::DemiBold);
+		fmt.setFont(font);
+		cursor.insertText(sTitle, fmt);
+		cursor.insertText("\n");
+		int nQuestionSize = jPaperDetails["paperDetailUnits"].size();
+		for (int j = 0; j < nQuestionSize; ++j)
+		{
+			Json::Value jQuestion = jPaperDetails["paperDetailUnits"][j];
+			QString sBody = QString::fromLocal8Bit("%1、%2(%3分)")
+				.arg(nIndex).arg(jQuestion["question"]["quesBody"].asString().c_str())
+				.arg(jQuestion["score"].asDouble());
+			
+			font.setPixelSize(g_appInfoPtr->m_fRate * 14);
+			font.setWeight(QFont::Medium);
+			fmt.setFont(font);
+			cursor.insertText(QString::fromLocal8Bit("%1、")
+				.arg(nIndex), fmt);
+			cursor.insertHtml(jQuestion["question"]["quesBody"].asString().c_str());
+			cursor.insertText(QString::fromLocal8Bit("(%1分)")
+				.arg(jQuestion["score"].asDouble()), fmt);
+			cursor.insertText("\n\r");
+		}
+	}
+	
+}
+
+void cloeViewPaper::on_btn_clop_goback_clicked()
+{
+    emit goback();
+}

+ 29 - 0
client/cloeViewPaper.h

@@ -0,0 +1,29 @@
+#ifndef CLOEVIEWPAPER_H
+#define CLOEVIEWPAPER_H
+
+#include <QWidget>
+
+namespace Ui {
+class cloeViewPaper;
+}
+
+class cloeViewPaper : public QWidget
+{
+    Q_OBJECT
+signals:
+    void goback();
+public:
+    explicit cloeViewPaper(QString sPaper, QWidget *parent = nullptr);
+    ~cloeViewPaper();
+
+private slots:
+    void on_btn_clop_goback_clicked();
+
+private:
+    void initUI();
+	void initPaper(QString sPaper);
+
+    Ui::cloeViewPaper *ui;    
+};
+
+#endif // CLOEVIEWPAPER_H

+ 179 - 0
client/cloeViewPaper.ui

@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>cloeViewPaper</class>
+ <widget class="QWidget" name="cloeViewPaper">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>942</width>
+    <height>646</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_awbg_BG" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>911</width>
+     <height>661</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_awbg_top" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>941</width>
+      <height>80</height>
+     </rect>
+    </property>
+    <widget class="QLabel" name="label_awbg_examIcon">
+     <property name="geometry">
+      <rect>
+       <x>10</x>
+       <y>20</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_awbg_exam">
+     <property name="geometry">
+      <rect>
+       <x>100</x>
+       <y>20</y>
+       <width>181</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="frameShape">
+      <enum>QFrame::NoFrame</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_clop_goback">
+     <property name="geometry">
+      <rect>
+       <x>480</x>
+       <y>20</y>
+       <width>75</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>返回列表</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QLabel" name="label_serverTime">
+    <property name="geometry">
+     <rect>
+      <x>310</x>
+      <y>590</y>
+      <width>191</width>
+      <height>20</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>服务器时间:2021-08-10 14:42:27</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_cl_version">
+    <property name="geometry">
+     <rect>
+      <x>510</x>
+      <y>590</y>
+      <width>81</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>版本号:V1.0</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_cl_company">
+    <property name="geometry">
+     <rect>
+      <x>20</x>
+      <y>590</y>
+      <width>241</width>
+      <height>16</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>技术支持:武汉启明泰和软件服务有限公司</string>
+    </property>
+   </widget>
+   <widget class="QWidget" name="widget_cloe_viewpaper" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>80</y>
+      <width>861</width>
+      <height>421</height>
+     </rect>
+    </property>
+    <widget class="QTextBrowser" name="tb_clopvp_paper">
+     <property name="geometry">
+      <rect>
+       <x>180</x>
+       <y>150</y>
+       <width>256</width>
+       <height>192</height>
+      </rect>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_HLine">
+     <property name="geometry">
+      <rect>
+       <x>350</x>
+       <y>120</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_clopvp_courseInfo">
+     <property name="geometry">
+      <rect>
+       <x>360</x>
+       <y>100</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_clopvp_paperName">
+     <property name="geometry">
+      <rect>
+       <x>360</x>
+       <y>70</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 536 - 0
client/clopPaperDetail.cpp

@@ -0,0 +1,536 @@
+#include "clopPaperDetail.h"
+#include "ui_clopPaperDetail.h"
+
+#include "CAppInfo.h"
+#include "questionBody.h"
+#include "audioPlay.h"
+#include "logproc.h"
+#include "CCommonTools.h"
+#include "awMsgBox.h"
+ 
+clopPaperDetail::clopPaperDetail(__int64 nExamRecordDataId, QString sCourseCode, bool bFromCache, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clopPaperDetail), m_sCourseCode(sCourseCode), m_nExamRecordDataId(nExamRecordDataId),m_bFromCache(bFromCache)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	qRegisterMetaType<CFindExamRecordDataEntity>("CFindExamRecordDataEntity");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnFindExamRecordDataEntity, this, &clopPaperDetail::onFindExamRecordDataEntity);
+	qRegisterMetaType<CGetExamRecordPaperStruct>("CGetExamRecordPaperStruct");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetExamRecordPaperStruct, this, &clopPaperDetail::onGetExamRecordPaperStruct);
+	qRegisterMetaType<CGetExamRecordQuestions>("CGetExamRecordQuestions");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetExamRecordQuestions, this, &clopPaperDetail::onGetExamRecordQuestions);
+	qRegisterMetaType<CGetQuestion>("CGetQuestion");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetQuestion, this, &clopPaperDetail::onGetQuestion);
+	{
+		CHttpRequestPackage hrp;
+		hrp.sUri = "/api/ecs_oe_admin/client/exam/process/getExamRecordPaperStruct";
+		hrp.sParamList.push_back(QString("examRecordDataId,%1").arg(nExamRecordDataId));
+        if(bFromCache)
+        {
+            hrp.sParamList.push_back(QString("fromCache,%1").arg(true));
+        }
+		hrp.nRequestType = RequestType::rtGetExamRecordPaperStruct;
+		hrp.eParamType = HttpParamType::hptUrl;
+		g_httpBllPtr->post(hrp);
+	}
+
+	{
+		CHttpRequestPackage hrp;
+		hrp.sUri = QString("/api/ecs_oe_admin/client/exam/process/findExamRecordDataEntity");
+		hrp.sParamList.push_back(QString("examRecordDataId,%1").arg(m_nExamRecordDataId));
+        if(bFromCache)
+        {
+            hrp.sParamList.push_back(QString("fromCache,%1").arg(true));
+        }
+		hrp.nRequestType = RequestType::rtFindExamRecordDataEntity;
+		hrp.eParamType = HttpParamType::hptUrl;
+		g_httpBllPtr->post(hrp);
+	}
+}
+
+clopPaperDetail::~clopPaperDetail()
+{
+    delete ui;
+}
+
+int clopPaperDetail::setUI(const int nWidth)
+{
+    ui->btn_expandPaper->setGeometry(g_appInfoPtr->m_fRate*30, 0, g_appInfoPtr->m_fRate*108, g_appInfoPtr->m_fRate*32);
+    ui->btn_expandPaper->setIconSize(QSize(g_appInfoPtr->m_fRate*14, g_appInfoPtr->m_fRate*14));
+    ui->btn_expandPaper->setIcon(QIcon(":/images/icon-expand-paper.png"));
+    ui->lw_paperDetail->setGeometry(ui->btn_expandPaper->x(), ui->btn_expandPaper->y() + ui->btn_expandPaper->height() + g_appInfoPtr->m_fRate*19,
+                                      nWidth - g_appInfoPtr->m_fRate*30*2, 200);
+    ui->lw_paperDetail->setVisible(false);
+    return ui->btn_expandPaper->y() + ui->btn_expandPaper->height() + g_appInfoPtr->m_fRate*20;
+}
+
+void clopPaperDetail::onFindExamRecordDataEntity(CFindExamRecordDataEntity findExamRecordDataEntity)
+{
+	if (findExamRecordDataEntity.nCode == 200)
+	{
+		m_nExamId = findExamRecordDataEntity.nExamId;
+		m_sPaperType = findExamRecordDataEntity.sPaperType;
+		CHttpRequestPackage hrp;
+		hrp.sUri = QString("/api/ecs_oe_admin/client/exam/process/getExamRecordQuestions");
+        hrp.sParamList.push_back(QString("examRecordDataId,%1").arg(m_nExamRecordDataId));
+        if(m_bFromCache)
+        {
+            hrp.sParamList.push_back(QString("fromCache,%1").arg(true));
+        }
+		hrp.nRequestType = RequestType::rtGetExamRecordQuestions;
+		hrp.eParamType = HttpParamType::hptUrl;
+		g_httpBllPtr->post(hrp);
+	}
+	else
+	{
+        if(findExamRecordDataEntity.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取考试记录失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(findExamRecordDataEntity.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clopPaperDetail::onGetExamRecordQuestions(CGetExamRecordQuestions getExamRecordQuestions)
+{
+	if (getExamRecordQuestions.nCode == 200)
+	{	
+		int nSize = getExamRecordQuestions.vExamRecordQuestions.size();
+		for (int i = 0; i < nSize; ++i)
+		{
+			CAnsweredQuestion aq = getExamRecordQuestions.vExamRecordQuestions[i];
+			setStudentAnswer(aq.nOrder, aq.sStudentAnswer);
+			CHttpRequestPackage hrp;
+			hrp.sUri = QString("/api/branch_ecs_ques/default_question/question/for/client");
+			hrp.sParamList.push_back(QString("questionId,%1").arg(aq.sQuestionId));
+			hrp.sParamList.push_back(QString("examId,%1").arg(m_nExamId));
+			hrp.sParamList.push_back(QString("courseCode,%1").arg(m_sCourseCode));
+			hrp.sParamList.push_back(QString("groupCode,%1").arg(m_sPaperType));
+			hrp.nRequestType = RequestType::rtGetQuestion;
+			hrp.eParamType = HttpParamType::hptBody;
+			g_httpBllPtr->post(hrp);
+		}
+	}
+	else
+	{
+        if(getExamRecordQuestions.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取题目信息失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(getExamRecordQuestions.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clopPaperDetail::setStudentAnswer(int nOrder, QString sStudentAnswer)
+{
+	for (CPaperGroupStruct &pgs : m_vPaperGroupStruct)
+	{
+		for (CQuestionStruct &qs : pgs.vQuestionStruct)
+		{
+			for (CSubQuestionStruct &sqs : qs.vSubQuestionStruct)
+			{
+				if (sqs.nOrder == nOrder)
+				{
+					sqs.sStudentAnswer = sStudentAnswer;
+					break;
+				}
+			}
+		}
+	}
+}
+
+void clopPaperDetail::onGetExamRecordPaperStruct(CGetExamRecordPaperStruct getExamRecordPaperStruct)
+{
+	if (getExamRecordPaperStruct.nCode == 200)
+	{
+		m_vPaperGroupStruct.swap(getExamRecordPaperStruct.vPaperGroupStruct);		
+	}
+	else
+	{
+        if(getExamRecordPaperStruct.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取试卷结构失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(getExamRecordPaperStruct.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clopPaperDetail::onGetQuestion(CGetQuestion getQuestion)
+{
+	if (getQuestion.nCode == 200)
+	{
+		for (CPaperGroupStruct &pgs : m_vPaperGroupStruct)
+		{
+			for (CQuestionStruct &qs : pgs.vQuestionStruct)
+			{
+				if (qs.sQuestionId == getQuestion.sId)
+				{
+					qs.bHasContent = true;
+					qs.sBody = getQuestion.sBody;
+					qs.sVersion = getQuestion.sVersion;
+					qs.bhasAudios = getQuestion.bhasAudios;
+					for (int i = 0; i < getQuestion.vSubQuestion.size(); ++i)
+					{
+						CSubQuestion sq = getQuestion.vSubQuestion[i];
+						qs.vSubQuestionStruct[i].sBody = sq.sBody;
+						qs.vSubQuestionStruct[i].vRightAnswer.swap(sq.vRightAnswer);
+						qs.vSubQuestionStruct[i].vOptions.swap(sq.vOptions);
+					}															
+					break;
+				}
+			}
+		}			
+	}
+	else
+	{
+        if(getQuestion.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取试题失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(getQuestion.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+void clopPaperDetail::on_btn_expandPaper_clicked()
+{
+	bool bShow = true;
+	for(CPaperGroupStruct &pgs : m_vPaperGroupStruct)
+	{
+		for (CQuestionStruct &qs : pgs.vQuestionStruct)
+		{
+			if (!qs.bHasContent)
+			{
+				CHttpRequestPackage hrp;
+				hrp.sUri = QString("/api/branch_ecs_ques/default_question/question/for/client");
+				hrp.sParamList.push_back(QString("questionId,%1").arg(qs.sQuestionId));
+				hrp.sParamList.push_back(QString("examId,%1").arg(m_nExamId));
+				hrp.sParamList.push_back(QString("courseCode,%1").arg(m_sCourseCode));
+				hrp.sParamList.push_back(QString("groupCode,%1").arg(m_sPaperType));
+				hrp.nRequestType = RequestType::rtGetQuestion;
+				hrp.eParamType = HttpParamType::hptBody;
+				g_httpBllPtr->post(hrp);
+				bShow = false;
+			}
+		}
+	}
+
+	if (bShow)
+	{
+		int nIndex = 1;
+		Json::Value jBody = Json::Value::null;
+		for (int i = 0; i < m_vPaperGroupStruct.size(); ++i)
+		{
+			CPaperGroupStruct pgs = m_vPaperGroupStruct[i];
+			QString sGroupBody = QString::fromLocal8Bit(R"({"sections":[{"blocks":[{"type":"text","value":"%1、%2"}]}]})")
+				.arg(CCommonTools::Arab2Sinogram(pgs.nNumber)).arg(pgs.sGroupName);
+			Json::Value blockItem = Json::Value::null;
+			blockItem["type"] = "text";
+			blockItem["value"] = (QString::fromLocal8Bit("%1、%2").arg(CCommonTools::Arab2Sinogram(pgs.nNumber)).arg(pgs.sGroupName)).toStdString();
+			Json::Value block = Json::Value::null;
+			block["blocks"].append(blockItem);
+			jBody["sections"].append(block);
+			for (CQuestionStruct &qs : pgs.vQuestionStruct)
+			{
+				if (!qs.sBody.isEmpty())
+				{
+					Json::Reader reader;
+					Json::Value jQuestion = Json::Value::null;
+					if (!reader.parse(qs.sBody.toStdString(), jQuestion))
+					{
+						//            ShowMsg(QString::fromLocal8Bit("解析题干出错!"), g_appInfoPtr->m_answerWidget);
+						myDebug() << QString::fromLocal8Bit("解析题干出错!");
+					}
+					for (int iq = 0; iq <  jQuestion["sections"].size(); ++iq)
+					{
+						jBody["sections"].append(jQuestion["sections"][iq]);
+					}
+					
+				}
+
+				for (CSubQuestionStruct &sqs : qs.vSubQuestionStruct)
+				{
+					if (!sqs.sBody.isEmpty())
+					{
+						Json::Reader reader;
+						Json::Value jSubQuestion = Json::Value::null;
+						if (!reader.parse(sqs.sBody.toStdString(), jSubQuestion))
+						{
+							//            ShowMsg(QString::fromLocal8Bit("解析题干出错!"), g_appInfoPtr->m_answerWidget);
+							myDebug() << QString::fromLocal8Bit("解析题干出错!");
+						}
+						if (jSubQuestion["sections"][0]["blocks"][0]["type"] == "text")
+						{
+							QString sValue = jSubQuestion["sections"][0]["blocks"][0]["value"].asString().c_str();
+							jSubQuestion["sections"][0]["blocks"][0]["value"] = (QString::fromLocal8Bit("%1、%2").arg(nIndex).arg(sValue)).toStdString();
+						}
+						else
+						{
+							Json::Value blockItem = Json::Value::null;
+							blockItem["type"] = "text";
+							blockItem["value"] = QString::fromLocal8Bit("% 1、").arg(nIndex).toStdString();
+							Json::Value block = Json::Value::null;
+							block["blocks"].append(blockItem);
+							jBody["sections"].append(block);
+						}
+						for (int isq = 0; isq < jSubQuestion["sections"].size(); ++isq)
+						{
+							jBody["sections"].append(jSubQuestion["sections"][isq]);
+						}
+					}
+					else
+					{
+						Json::Value blockItem = Json::Value::null;
+						blockItem["type"] = "text";
+						blockItem["value"] = QString::fromLocal8Bit("% 1、").arg(nIndex).toStdString();
+						Json::Value block = Json::Value::null;
+						block["blocks"].append(blockItem);
+						jBody["sections"].append(block);
+					}
+					nIndex++;
+					for (int io = 0; io < sqs.vOptions.size(); ++io)
+					{
+						QString sOption = sqs.vOptions[io].sBody;
+						Json::Reader reader;
+						Json::Value jOption = Json::Value::null;
+						if (!reader.parse(sOption.toStdString(), jOption))
+						{
+							//            ShowMsg(QString::fromLocal8Bit("解析题干出错!"), g_appInfoPtr->m_answerWidget);
+							myDebug() << QString::fromLocal8Bit("解析题干出错!");
+						}
+
+						if (jOption["sections"][0]["blocks"][0]["type"] == "text")
+						{
+							QString sValue = jOption["sections"][0]["blocks"][0]["value"].asString().c_str();
+							jOption["sections"][0]["blocks"][0]["value"] = (QString::fromLocal8Bit("%1、%2").arg(QChar('A' + io)).arg(sValue)).toStdString();
+						}
+						else
+						{
+							Json::Value blockItem = Json::Value::null;
+							blockItem["type"] = "text";
+							blockItem["value"] = QString::fromLocal8Bit("% 1、").arg(nIndex).toStdString();
+							Json::Value block = Json::Value::null;
+							block["blocks"].append(blockItem);
+							jBody["sections"].append(block);
+						}
+
+						for (int iop = 0; iop < jOption["sections"].size(); ++iop)
+						{
+							jBody["sections"].append(jOption["sections"][iop]);
+						}
+					}
+
+
+
+					int nRA = sqs.vRightAnswer.size();
+					QString sRightAnswer = "";
+					for (int ira = 0; ira < nRA; ++ira)
+					{
+						if(sqs.sQuestionType == QUESTION_TYPE::SingleChoice ||
+							sqs.sQuestionType == QUESTION_TYPE::MultipleChoice ||
+							sqs.sQuestionType == QUESTION_TYPE::TrueOrFalse)
+						{ 
+                            Json::Reader reader;
+                            Json::Value jRightAnswer = Json::Value::null;
+                            if (!reader.parse(sqs.vRightAnswer[ira].toStdString(), jRightAnswer))
+                            {
+                                //            ShowMsg(QString::fromLocal8Bit("解析题干出错!"), g_appInfoPtr->m_answerWidget);
+                                myDebug() << QString::fromLocal8Bit("解析题干出错!");
+                            }
+
+                            for(int nrai = 0; nrai < jRightAnswer["sections"].size(); ++nrai)
+                            {
+                               Json::Value jBlocks =  jRightAnswer["sections"][nrai];
+                               for(int nb = 0; nb < jBlocks["blocks"].size();  nb++)
+                               {
+                                    if(jBlocks["blocks"][nb]["type"] == "text")
+                                    {
+                                        if(sqs.sQuestionType != QUESTION_TYPE::TrueOrFalse)
+                                        {
+                                            sRightAnswer += 'A' + QString(jBlocks["blocks"][nb]["value"].asString().c_str()).toInt();
+                                        }
+                                        else
+                                        {
+                                            sRightAnswer += jBlocks["blocks"][nb]["value"].asString() == "true" ? QString::fromLocal8Bit("正确") : QString::fromLocal8Bit("错误");
+                                        }
+                                    }
+                               }                                
+                            }
+
+						}
+						else if (sqs.sQuestionType == QUESTION_TYPE::FillUp ||
+							sqs.sQuestionType == QUESTION_TYPE::Essay)
+						{
+                            sRightAnswer = sqs.vRightAnswer[ira];
+                            Json::Reader reader;
+                            Json::Value jRightAnswer = Json::Value::null;
+                            if (!reader.parse(sRightAnswer.toStdString(), jRightAnswer))
+                            {
+                                //            ShowMsg(QString::fromLocal8Bit("解析题干出错!"), g_appInfoPtr->m_answerWidget);
+                                myDebug() << QString::fromLocal8Bit("解析题干出错!");
+                            }
+                            if(ira == 0)
+                            {
+                                Json::Value blockItemAns = Json::Value::null;
+                                blockItemAns["type"] = "text";
+                                blockItemAns["value"] = QString::fromLocal8Bit("正确答案:").toStdString();
+                                Json::Value blockAns = Json::Value::null;
+                                blockAns["blocks"].append(blockItemAns);
+                                jBody["sections"].append(blockAns);
+                            }
+                            for(int nrai = 0; nrai < jRightAnswer["sections"].size(); ++nrai)
+                            {
+                                jBody["sections"].append(jRightAnswer["sections"][nrai]);
+                            }
+						}
+					}
+
+                    if(sqs.sQuestionType == QUESTION_TYPE::SingleChoice ||
+                        sqs.sQuestionType == QUESTION_TYPE::MultipleChoice ||
+                        sqs.sQuestionType == QUESTION_TYPE::TrueOrFalse)
+                    {
+                        Json::Value blockItemAns = Json::Value::null;
+                        blockItemAns["type"] = "text";
+                        blockItemAns["value"] = (QString::fromLocal8Bit("正确答案:%1").arg(sRightAnswer)).toStdString();
+                        Json::Value blockAns = Json::Value::null;
+                        blockAns["blocks"].append(blockItemAns);
+                        jBody["sections"].append(blockAns);
+                    }
+
+
+
+                    QString sStudentAnswer = "";
+
+                    if (sqs.sQuestionType == QUESTION_TYPE::SingleChoice ||
+                        sqs.sQuestionType == QUESTION_TYPE::MultipleChoice ||
+                        sqs.sQuestionType == QUESTION_TYPE::TrueOrFalse)
+                    {
+                        for (int isal = 0; isal < sqs.sStudentAnswer.length(); ++isal)
+                        {
+                            sStudentAnswer += 'A' + QString(sqs.sStudentAnswer[isal]).toInt();
+                        }
+                    }
+                    else if (sqs.sQuestionType == QUESTION_TYPE::FillUp ||
+                        sqs.sQuestionType == QUESTION_TYPE::Essay)
+                    {
+                        sStudentAnswer += sqs.sStudentAnswer;
+                    }
+
+                    Json::Value blockItemStuAns = Json::Value::null;
+                    blockItemStuAns["type"] = "text";
+                    blockItemStuAns["value"] = (QString::fromLocal8Bit("考生答案:%1").arg(sStudentAnswer)).toStdString();
+                    Json::Value blockStuAns = Json::Value::null;
+                    blockStuAns["blocks"].append(blockItemStuAns);
+                    jBody["sections"].append(blockStuAns);
+
+					Json::Value blockItem = Json::Value::null;
+					blockItem["type"] = "text";
+					blockItem["value"] = "";
+					Json::Value block = Json::Value::null;
+					block["blocks"].append(blockItem);
+					jBody["sections"].append(block);
+				}
+			}
+		}
+
+		initQuestion(jBody);
+	}
+}
+
+void clopPaperDetail::initQuestion(Json::Value jBody)
+{
+	int nListHeight = 0;
+	int nIndex = 1;
+	int nSectionSize = jBody["sections"].size();
+	Json::Value jSections = Json::Value::null;
+	for (int i = 0; i < nSectionSize; i++)
+	{
+		Json::Value jblocks = Json::Value::null;
+		int nBlockSize = jBody["sections"][i]["blocks"].size();
+		for (int j = 0; j < nBlockSize; j++)
+		{
+			if (jBody["sections"][i]["blocks"][j]["type"].asString() == "audio")
+			{
+				if (jblocks != Json::Value::null ||
+					jSections != Json::Value::null)
+				{
+					if (jblocks != Json::Value::null)
+					{
+						Json::Value jBlockItem = Json::Value::null;
+						jBlockItem["blocks"] = jblocks;
+						jblocks = Json::Value::null;
+						jSections.append(jBlockItem);
+					}
+					Json::Value jQuestionBody = Json::Value::null;
+					jQuestionBody["sections"] = jSections;
+					jSections = Json::Value::null;
+					jblocks = Json::Value::null;
+					questionBody *wQB = new questionBody(jQuestionBody);
+					QListWidgetItem *listItem = new QListWidgetItem;
+					int nQBHeight = wQB->setUI(ui->lw_paperDetail->width() - 40, nIndex);
+					listItem->setSizeHint(QSize(ui->lw_paperDetail->width() - 40, nQBHeight));
+					ui->lw_paperDetail->addItem(listItem);
+					ui->lw_paperDetail->setItemWidget(listItem, wQB);
+					nListHeight += nQBHeight;
+				}
+
+				nIndex++;
+				QString sAudioName = jBody["sections"][i]["blocks"][j]["ext2"].asString().c_str();
+				QString sAudioUrl = jBody["sections"][i]["blocks"][j]["value"].asString().c_str();
+				audioPlay *wAP = new audioPlay(sAudioName, sAudioUrl, 10);
+
+				QListWidgetItem *listItem = new QListWidgetItem;
+				int nAPHeight = wAP->setUI(ui->lw_paperDetail->width() - 40);
+				listItem->setSizeHint(QSize(ui->lw_paperDetail->width() - 40, nAPHeight));
+				ui->lw_paperDetail->addItem(listItem);
+				ui->lw_paperDetail->setItemWidget(listItem, wAP);
+				nListHeight += nAPHeight;
+			}
+			else
+			{
+				jblocks.append(jBody["sections"][i]["blocks"][j]);
+			}
+		}
+		if (jblocks != Json::Value::null)
+		{
+			Json::Value jBlockItem = Json::Value::null;
+			jBlockItem["blocks"] = jblocks;
+			jblocks = Json::Value::null;
+			jSections.append(jBlockItem);
+		}
+	}
+
+	if (jSections != Json::Value::null)
+	{
+		Json::Value jQuestionBody = Json::Value::null;
+		jQuestionBody["sections"] = jSections;
+		jSections = Json::Value::null;
+		questionBody *wQB = new questionBody(jQuestionBody);
+		QListWidgetItem *listItem = new QListWidgetItem;
+		int nQBHeight = wQB->setUI(ui->lw_paperDetail->width() - 40, nIndex);
+		listItem->setSizeHint(QSize(ui->lw_paperDetail->width() - 40, nQBHeight));
+		ui->lw_paperDetail->addItem(listItem);
+		ui->lw_paperDetail->setItemWidget(listItem, wQB);
+		nListHeight += nQBHeight;
+	}
+	
+	ui->lw_paperDetail->setGeometry(ui->btn_expandPaper->x(), ui->btn_expandPaper->y() + ui->btn_expandPaper->height() + g_appInfoPtr->m_fRate * 19,
+		width() - g_appInfoPtr->m_fRate * 30 * 2, nListHeight);
+	ui->lw_paperDetail->setVisible(true);
+	
+	emit heightChange(ui->lw_paperDetail->y() + ui->lw_paperDetail->height() + g_appInfoPtr->m_fRate * 20);
+}

+ 43 - 0
client/clopPaperDetail.h

@@ -0,0 +1,43 @@
+#ifndef CLOPPAPERDETAIL_H
+#define CLOPPAPERDETAIL_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+
+namespace Ui {
+class clopPaperDetail;
+}
+
+class clopPaperDetail : public QWidget
+{
+    Q_OBJECT
+signals:
+	void heightChange(int nHeight);
+public:
+    explicit clopPaperDetail(__int64 nExamRecordDataId, QString sCourseCode, bool bFromCache = false, QWidget *parent = nullptr);
+    ~clopPaperDetail();
+
+    int setUI(const int nWidth);
+private slots:
+    void on_btn_expandPaper_clicked();
+
+	void onFindExamRecordDataEntity(CFindExamRecordDataEntity findExamRecordDataEntity);
+	void onGetExamRecordPaperStruct(CGetExamRecordPaperStruct getExamRecordPaperStruct);
+	void onGetExamRecordQuestions(CGetExamRecordQuestions getExamRecordQuestions);
+	void onGetQuestion(CGetQuestion getQuestion);
+private:
+	void initQuestion(Json::Value jBody);
+	void setStudentAnswer(int nOrder, QString sStudentAnswer);
+
+    Ui::clopPaperDetail *ui;
+
+	__int64 m_nExamId;
+	QString m_sPaperType;
+	__int64 m_nExamRecordDataId;
+	QString m_sCourseCode;
+	
+	std::vector<CPaperGroupStruct> m_vPaperGroupStruct;
+    bool m_bFromCache;
+};
+
+#endif // CLOPPAPERDETAIL_H

+ 42 - 0
client/clopPaperDetail.ui

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clopPaperDetail</class>
+ <widget class="QWidget" name="clopPaperDetail">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QPushButton" name="btn_expandPaper">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>30</y>
+     <width>75</width>
+     <height>23</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>展开试卷</string>
+   </property>
+  </widget>
+  <widget class="QListWidget" name="lw_paperDetail">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>70</y>
+     <width>256</width>
+     <height>192</height>
+    </rect>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 149 - 0
client/clopPaperReport.cpp

@@ -0,0 +1,149 @@
+#include "clopPaperReport.h"
+#include "ui_clopPaperReport.h"
+
+#include "CAppInfo.h"
+
+#include <QScrollBar>
+#include "awMsgBox.h"
+
+clopPaperReport::clopPaperReport(__int64 nExamRecordDataId, bool bFromCache, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clopPaperReport)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	qRegisterMetaType<CGetPracticeDetailInfo>("CGetPracticeDetailInfo");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetPracticeDetailInfo, this, &clopPaperReport::onGetPracticeDetailInfo);
+
+
+	CHttpRequestPackage hrp;
+	hrp.sUri = QString("/api/ecs_oe_admin/client/exam/process/getPracticeDetailInfo");
+	hrp.sParamList.push_back(QString("examRecordDataId,%1").arg(nExamRecordDataId));
+    if(bFromCache)
+    {
+        hrp.sParamList.push_back(QString("fromCache,%1").arg(true));
+    }
+	hrp.nRequestType = RequestType::rtGetPracticeDetailInfo;
+	hrp.eParamType = HttpParamType::hptUrl;
+	g_httpBllPtr->post(hrp);
+}
+
+clopPaperReport::~clopPaperReport()
+{
+    delete ui;
+}
+
+int clopPaperReport::setUI(const int nWidth)
+{
+    ui->label_clop_tips_title->adjustSize();
+    ui->label_clop_tips_title->setGeometry(g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*20,
+                                           ui->label_clop_tips_title->width(), ui->label_clop_tips_title->height());
+    ui->label_clop_tips->setFixedWidth(nWidth - g_appInfoPtr->m_fRate*30*2);
+    ui->label_clop_tips->adjustSize();
+    ui->label_clop_tips->setGeometry(ui->label_clop_tips_title->x(), ui->label_clop_tips_title->y() + ui->label_clop_tips_title->height() + g_appInfoPtr->m_fRate*5,
+                                     ui->label_clop_tips->width(), ui->label_clop_tips->height());
+
+    ui->tablewt_examList->setGeometry(g_appInfoPtr->m_fRate*30, ui->label_clop_tips->y() + ui->label_clop_tips->height() + g_appInfoPtr->m_fRate*20,
+                                      nWidth - g_appInfoPtr->m_fRate*30*2,
+		(ui->tablewt_examList->rowCount() + 1)*g_appInfoPtr->m_fRate * 48);
+    ui->tablewt_examList->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablewt_examList->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablewt_examList->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablewt_examList->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablewt_examList->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablewt_examList->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablewt_examList->setRowCount(10);
+    //设置列数
+    ui->tablewt_examList->setColumnCount(5);
+
+    ui->tablewt_examList->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablewt_examList->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+    QStringList sHeadStr;
+    sHeadStr << QString::fromLocal8Bit("题型") <<
+                QString::fromLocal8Bit("题量") <<
+                QString::fromLocal8Bit("正确") <<
+                QString::fromLocal8Bit("错误") <<
+                QString::fromLocal8Bit("未答");
+
+    ui->tablewt_examList->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablewt_examList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablewt_examList->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablewt_examList->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    int nColWidth = ui->tablewt_examList->width()/5;
+    ui->tablewt_examList->setColumnWidth(0, nColWidth);
+    ui->tablewt_examList->setColumnWidth(1, nColWidth);
+    ui->tablewt_examList->setColumnWidth(2, nColWidth);
+    ui->tablewt_examList->setColumnWidth(3, nColWidth);
+    ui->tablewt_examList->setColumnWidth(4, nColWidth);
+    ui->tablewt_examList->horizontalHeader()->setStretchLastSection(true);
+
+    return ui->tablewt_examList->y() + ui->tablewt_examList->height() + g_appInfoPtr->m_fRate*20;
+}
+
+QString clopPaperReport::getQuestionName(QString questionType)
+{
+    if(questionType == QUESTION_TYPE::SingleChoice)
+    {
+        return QString::fromLocal8Bit("单选题");
+    }
+    else if(questionType == QUESTION_TYPE::MultipleChoice)
+    {
+        return QString::fromLocal8Bit("多选题");
+    }
+    else if(questionType == QUESTION_TYPE::TrueOrFalse)
+    {
+        return QString::fromLocal8Bit("判断题");
+    }
+    else if(questionType == QUESTION_TYPE::FillUp)
+    {
+        return QString::fromLocal8Bit("填空题");
+    }
+    else if(questionType == QUESTION_TYPE::Essay)
+    {
+        return QString::fromLocal8Bit("问答题");
+    }
+}
+
+void clopPaperReport::onGetPracticeDetailInfo(CGetPracticeDetailInfo getPracticeDetailInfo)
+{
+    if(getPracticeDetailInfo.nCode == 200)
+    {        
+        int nSize = getPracticeDetailInfo.vPracticePaperStructList.size();
+		ui->tablewt_examList->setRowCount(nSize);
+        for(int i = 0; i < nSize; i++)
+        {
+            CPracticePaperStructInfo pps = getPracticeDetailInfo.vPracticePaperStructList[i];
+            ui->tablewt_examList->setItem(i, 0, new QTableWidgetItem(pps.sTitle));
+            ui->tablewt_examList->setItem(i, 1, new QTableWidgetItem(QString::number(pps.nQuestionCount)));
+            ui->tablewt_examList->setItem(i, 2, new QTableWidgetItem(QString::number(pps.nSuccQuestionNum)));
+            ui->tablewt_examList->setItem(i, 3, new QTableWidgetItem(QString::number(pps.nFailQuestionNum)));
+            ui->tablewt_examList->setItem(i, 4, new QTableWidgetItem(QString::number(pps.nNotAnsweredCount)));
+        }
+        int nHeight =  ui->tablewt_examList->y() + (nSize+1)*g_appInfoPtr->m_fRate*48 + g_appInfoPtr->m_fRate*20;
+		ui->tablewt_examList->setGeometry(g_appInfoPtr->m_fRate * 30, ui->label_clop_tips->y() + ui->label_clop_tips->height() + g_appInfoPtr->m_fRate * 20,
+			width() - g_appInfoPtr->m_fRate * 30 * 2,
+			(ui->tablewt_examList->rowCount()+1)*g_appInfoPtr->m_fRate * 48) ;
+        emit heightChange(nHeight);
+    }
+    else
+    {
+        if(getPracticeDetailInfo.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取练习信息失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(getPracticeDetailInfo.sMessage, (QWidget*)(this->parent()));
+        }
+    }
+}

+ 29 - 0
client/clopPaperReport.h

@@ -0,0 +1,29 @@
+#ifndef CLOPPAPERREPORT_H
+#define CLOPPAPERREPORT_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+
+namespace Ui {
+class clopPaperReport;
+}
+
+class clopPaperReport : public QWidget
+{
+    Q_OBJECT
+signals:
+    void heightChange(int nHeight);
+public:
+    explicit clopPaperReport(__int64 nExamRecordDataId, bool bFromCache = false, QWidget *parent = nullptr);
+    ~clopPaperReport();
+
+    int setUI(const int nWidth);
+private slots:
+    void onGetPracticeDetailInfo(CGetPracticeDetailInfo getPracticeDetailInfo);
+private:
+    QString getQuestionName(QString questionType);
+
+    Ui::clopPaperReport *ui;
+};
+
+#endif // CLOPPAPERREPORT_H

+ 59 - 0
client/clopPaperReport.ui

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clopPaperReport</class>
+ <widget class="QWidget" name="clopPaperReport">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>636</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QLabel" name="label_clop_tips_title">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>30</y>
+     <width>54</width>
+     <height>12</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>报告提示:</string>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_clop_tips">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>60</y>
+     <width>581</width>
+     <height>41</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>1. 若本练习卷中包含部分主观题型,则报告统计数据仅供参考,因为部分题型需考生根据参考答案判断正确和错误,报告仅对可统计的提醒进行统计。
+2. 若单题存在多处作答,只要有一处出错,改题就判未错题。</string>
+   </property>
+   <property name="wordWrap">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="QTableWidget" name="tablewt_examList">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>110</y>
+     <width>871</width>
+     <height>401</height>
+    </rect>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 105 - 0
client/clopReport.cpp

@@ -0,0 +1,105 @@
+#include "clopReport.h"
+#include "ui_clopReport.h"
+
+#include "CAppInfo.h"
+#include "clOperation.h"
+#include "clopPaperReport.h"
+#include "clopPaperDetail.h"
+
+#include <QScrollBar>
+
+clopReport::clopReport(CPracticeRecord pr, bool bFromCache, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clopReport), m_pr(pr), m_bFromCache(bFromCache)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+	
+	ui->lw_reportInfo->setVerticalScrollMode(QListWidget::ScrollPerPixel);
+	ui->lw_reportInfo->verticalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+    ui->label_clop_course->setText(QString::fromLocal8Bit("/  科目:%1").arg(pr.sCourseName));
+    ui->label_clop_correctRate->setText(QString::fromLocal8Bit("%1%").arg(pr.fObjectiveAccuracy));
+}
+
+clopReport::~clopReport()
+{
+    delete ui;
+}
+
+void clopReport::setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight)
+{
+    setGeometry(nLeft, nTop, nWidth, nHeight);
+    ui->widget_clop_BG->setGeometry(0, 0, width(), height());
+    ui->widget_clop_top->setGeometry(0, 0, width(), g_appInfoPtr->m_fRate*56);
+    ui->btn_clop_goback->setGeometry(ui->widget_clop_top->width() - g_appInfoPtr->m_fRate*(108 + 30), (ui->widget_clop_top->height() - g_appInfoPtr->m_fRate*32)/2, g_appInfoPtr->m_fRate*108, g_appInfoPtr->m_fRate*32);
+    ui->btn_clop_goback->setIconSize(QSize(g_appInfoPtr->m_fRate*14, g_appInfoPtr->m_fRate*14));
+    ui->btn_clop_goback->setIcon(QIcon(":/images/icon-goback-list.png"));
+    ui->label_clop_reportTitle->adjustSize();
+    ui->label_clop_reportTitle->setGeometry(g_appInfoPtr->m_fRate*30, (ui->widget_clop_top->height() - ui->label_clop_reportTitle->height())/2, ui->label_clop_reportTitle->width(), ui->label_clop_reportTitle->height());
+    ui->label_clop_course->adjustSize();
+    ui->label_clop_course->setGeometry(ui->label_clop_reportTitle->x() + ui->label_clop_reportTitle->width() + g_appInfoPtr->m_fRate*10,
+                                       (ui->widget_clop_top->height() - ui->label_clop_course->height())/2,
+                                       ui->label_clop_course->width(), ui->label_clop_course->height());
+    ui->label_clop_correctRate->adjustSize();
+    ui->label_clop_correctRate->setGeometry(ui->btn_clop_goback->x() - g_appInfoPtr->m_fRate*30 - ui->label_clop_correctRate->width(),
+                                            (ui->widget_clop_top->height() - ui->label_clop_correctRate->height())/2,
+                                            ui->label_clop_correctRate->width(), ui->label_clop_correctRate->height());
+    ui->label_clop_correctRateHint->adjustSize();
+    ui->label_clop_correctRateHint->setGeometry(ui->label_clop_correctRate->x() - ui->label_clop_correctRateHint->width() - g_appInfoPtr->m_fRate*10,
+                                                (ui->widget_clop_top->height() - ui->label_clop_correctRateHint->height())/2,
+                                                ui->label_clop_correctRateHint->width(), ui->label_clop_correctRateHint->height());
+    ui->lw_reportInfo->setGeometry(0, ui->widget_clop_top->height(), width(), height() - ui->widget_clop_top->height());
+
+    {
+        clopPaperReport *paperReport = new clopPaperReport(m_pr.nId, m_bFromCache);
+		connect(paperReport, &clopPaperReport::heightChange, this, [&](int nReportHeight) {
+			QListWidgetItem *pItem = ui->lw_reportInfo->item(0);
+			if (pItem)
+			{
+				QSize size = pItem->sizeHint();
+				size.setHeight(nReportHeight);
+				pItem->setSizeHint(size);
+			}
+		});
+
+        QListWidgetItem *pItem = new QListWidgetItem;
+        ui->lw_reportInfo->addItem(pItem);
+        ui->lw_reportInfo->setItemWidget(pItem, paperReport);
+
+        int npaperReportHeight = paperReport->setUI(ui->lw_reportInfo->width() - g_appInfoPtr->m_fRate*60);
+        QSize size = pItem->sizeHint();
+        size.setHeight(npaperReportHeight);
+        pItem->setSizeHint(size);
+    }
+
+    {
+        clopPaperDetail *paperDetail = new clopPaperDetail(m_pr.nId, m_pr.sCourseCode, m_bFromCache);
+		connect(paperDetail, &clopPaperDetail::heightChange, this, [&](int nReportHeight) {
+			QListWidgetItem *pItem = ui->lw_reportInfo->item(1);
+			if (pItem)
+			{
+				QSize size = pItem->sizeHint();
+				size.setHeight(nReportHeight);
+				pItem->setSizeHint(size);
+			}
+		});
+        QListWidgetItem *pItem = new QListWidgetItem;
+        ui->lw_reportInfo->addItem(pItem);
+        ui->lw_reportInfo->setItemWidget(pItem, paperDetail);
+
+        int nPaperDetailHeight = paperDetail->setUI(ui->lw_reportInfo->width() - g_appInfoPtr->m_fRate*60);
+        QSize size = pItem->sizeHint();
+        size.setHeight(nPaperDetailHeight);
+        pItem->setSizeHint(size);
+    }
+}
+
+
+
+
+void clopReport::on_btn_clop_goback_clicked()
+{
+    emit goback();
+}

+ 31 - 0
client/clopReport.h

@@ -0,0 +1,31 @@
+#ifndef CLOPREPORT_H
+#define CLOPREPORT_H
+
+#include <QWidget>
+
+#include "CHttpBll.h"
+
+namespace Ui {
+class clopReport;
+}
+
+class clopReport : public QWidget
+{
+    Q_OBJECT
+signals:
+    void goback();
+public:
+    explicit clopReport(CPracticeRecord pr, bool bFromCache = false, QWidget *parent = nullptr);
+    ~clopReport();
+
+    void setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight);
+private slots:
+    void on_btn_clop_goback_clicked();
+
+private:
+    Ui::clopReport *ui;
+    CPracticeRecord m_pr;
+    bool m_bFromCache;
+};
+
+#endif // CLOPREPORT_H

+ 117 - 0
client/clopReport.ui

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clopReport</class>
+ <widget class="QWidget" name="clopReport">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>844</width>
+    <height>584</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_clop_BG" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>40</y>
+     <width>871</width>
+     <height>571</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_clop_top" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>931</width>
+      <height>80</height>
+     </rect>
+    </property>
+    <property name="styleSheet">
+     <string notr="true"/>
+    </property>
+    <widget class="QLabel" name="label_clop_reportTitle">
+     <property name="geometry">
+      <rect>
+       <x>40</x>
+       <y>30</y>
+       <width>81</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>练习情况概率</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_clop_course">
+     <property name="geometry">
+      <rect>
+       <x>160</x>
+       <y>30</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>/  科目:标准的研制与编制</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_clop_goback">
+     <property name="geometry">
+      <rect>
+       <x>700</x>
+       <y>30</y>
+       <width>75</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>返回列表</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_clop_correctRateHint">
+     <property name="geometry">
+      <rect>
+       <x>340</x>
+       <y>30</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>答题正确率</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_clop_correctRate">
+     <property name="geometry">
+      <rect>
+       <x>420</x>
+       <y>30</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>50%</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QListWidget" name="lw_reportInfo">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>90</y>
+      <width>256</width>
+      <height>192</height>
+     </rect>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 167 - 0
client/clopResultList.cpp

@@ -0,0 +1,167 @@
+#include "clopResultList.h"
+#include "ui_clopResultList.h"
+
+#include "CAppInfo.h"
+#include "clOperation.h"
+
+#include <QScrollBar>
+#include "awMsgBox.h"
+
+clopResultList::clopResultList(CPracticeCourseInfo pci, QString sBatchName, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::clopResultList)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+    qRegisterMetaType<CQueryPracticeRecordList>("CQueryPracticeRecordList");
+    connect(g_httpBllPtr.get(), &CHttpBll::sgnQueryPracticeRecordList, this, &clopResultList::onQueryPracticeRecordList);
+
+    ui->label_clop_batch->setText(QString::fromLocal8Bit("批次:%1").arg(sBatchName));
+    ui->label_clop_course->setText(QString::fromLocal8Bit("科目:%1").arg(pci.sCourseName));
+    ui->label_clop_avgCorrectRate->setText(QString::fromLocal8Bit("平均正确率:%1").arg(pci.fAveObjectiveAccuracy));
+    ui->label_clop_maxCorrectRate->setText(QString::fromLocal8Bit("最高正确率:%1").arg(pci.fMaxObjectiveAccuracy));
+
+    CHttpRequestPackage hrp;
+    hrp.sUri = QString("/api/ecs_oe_admin/client/exam/process/queryPracticeRecordList");
+    hrp.sParamList.push_back(QString("examStudentId,%1").arg(pci.nExamStudentId));
+    hrp.nRequestType = RequestType::rtQueryPracticeRecordList;
+    hrp.eParamType = HttpParamType::hptUrl;
+    g_httpBllPtr->post(hrp);
+}
+
+clopResultList::~clopResultList()
+{
+    delete ui;
+}
+
+void clopResultList::setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight)
+{
+    setGeometry(nLeft, nTop, nWidth, nHeight);
+    ui->widget_clop_BG->setGeometry(0, 0, width(), height());
+    ui->widget_clop_top->setGeometry(0, 0, width(), g_appInfoPtr->m_fRate*56);
+    ui->btn_clop_goback->setGeometry(ui->widget_clop_top->width() - g_appInfoPtr->m_fRate*(108 + 30), (ui->widget_clop_top->height() - g_appInfoPtr->m_fRate*32)/2, g_appInfoPtr->m_fRate*108, g_appInfoPtr->m_fRate*32);
+    ui->btn_clop_goback->setIconSize(QSize(g_appInfoPtr->m_fRate*14, g_appInfoPtr->m_fRate*14));
+    ui->btn_clop_goback->setIcon(QIcon(":/images/icon-goback-list.png"));
+    ui->label_clop_batch->adjustSize();
+    ui->label_clop_batch->setGeometry(g_appInfoPtr->m_fRate*30, (ui->widget_clop_top->height() - ui->label_clop_batch->height())/2,
+                                 ui->label_clop_batch->width(), ui->label_clop_batch->height());
+    ui->label_clop_maxCorrectRate->adjustSize();
+    ui->label_clop_maxCorrectRate->setGeometry(ui->btn_clop_goback->x() - g_appInfoPtr->m_fRate*30 - ui->label_clop_maxCorrectRate->width(),
+                                               ui->label_clop_batch->y(), ui->label_clop_maxCorrectRate->width(), ui->label_clop_maxCorrectRate->height());
+    ui->label_clop_course->adjustSize();
+    ui->label_clop_avgCorrectRate->adjustSize();
+    int nLabelSpace = (ui->label_clop_maxCorrectRate->x() - ui->label_clop_batch->x() - ui->label_clop_batch->width() - ui->label_clop_course->width() - ui->label_clop_avgCorrectRate->width())/3;
+    ui->label_clop_course->setGeometry(ui->label_clop_batch->x() + ui->label_clop_batch->width() + nLabelSpace,
+                                       ui->label_clop_batch->y(), ui->label_clop_course->width(), ui->label_clop_course->height());
+    ui->label_clop_avgCorrectRate->setGeometry(ui->label_clop_course->x() + ui->label_clop_course->width() + nLabelSpace,
+                                               ui->label_clop_batch->y(), ui->label_clop_avgCorrectRate->width(), ui->label_clop_avgCorrectRate->height());
+    ui->tablewt_examList->setGeometry(g_appInfoPtr->m_fRate*30, ui->widget_clop_top->height() + g_appInfoPtr->m_fRate*10,
+                                      ui->widget_clop_BG->width() - g_appInfoPtr->m_fRate*30*2,
+                                      ui->widget_clop_BG->height() - ui->widget_clop_top->height() - g_appInfoPtr->m_fRate*(10+78));
+    ui->tablewt_examList->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablewt_examList->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablewt_examList->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablewt_examList->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablewt_examList->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablewt_examList->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablewt_examList->setRowCount(10);
+    //设置列数
+    ui->tablewt_examList->setColumnCount(10);
+
+    ui->tablewt_examList->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablewt_examList->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+    QStringList sHeadStr;
+    sHeadStr << QString::fromLocal8Bit("练习编号") <<
+                QString::fromLocal8Bit("开始日期") <<
+                QString::fromLocal8Bit("结束日期") <<
+                QString::fromLocal8Bit("练习时长") <<
+                QString::fromLocal8Bit("总题量") <<
+                QString::fromLocal8Bit("正确") <<
+                QString::fromLocal8Bit("错误") <<
+                QString::fromLocal8Bit("未答") <<
+                QString::fromLocal8Bit("正确率") <<
+                QString::fromLocal8Bit("操作");
+
+    ui->tablewt_examList->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablewt_examList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablewt_examList->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablewt_examList->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*52);
+    ui->tablewt_examList->setColumnWidth(0, g_appInfoPtr->m_fRate*92);
+    ui->tablewt_examList->setColumnWidth(1, g_appInfoPtr->m_fRate*172);
+    ui->tablewt_examList->setColumnWidth(2, g_appInfoPtr->m_fRate*172);
+    ui->tablewt_examList->setColumnWidth(3, g_appInfoPtr->m_fRate*97);
+    ui->tablewt_examList->setColumnWidth(4, g_appInfoPtr->m_fRate*90);
+    ui->tablewt_examList->setColumnWidth(5, g_appInfoPtr->m_fRate*90);
+    ui->tablewt_examList->setColumnWidth(6, g_appInfoPtr->m_fRate*90);
+    ui->tablewt_examList->setColumnWidth(7, g_appInfoPtr->m_fRate*90);
+    ui->tablewt_examList->setColumnWidth(8, g_appInfoPtr->m_fRate*90);
+    ui->tablewt_examList->setColumnWidth(9, g_appInfoPtr->m_fRate*128);
+    ui->tablewt_examList->horizontalHeader()->setStretchLastSection(true);
+}
+
+void clopResultList::onQueryPracticeRecordList(CQueryPracticeRecordList queryPracticeRecordList)
+{
+    if(queryPracticeRecordList.nCode == 200)
+    {
+        int nSize = queryPracticeRecordList.vPracticeRecordList.size();
+        ui->tablewt_examList->setRowCount(nSize);
+        for(int i = 0; i < nSize; ++i)
+        {
+            CPracticeRecord pr = queryPracticeRecordList.vPracticeRecordList[i];
+            ui->tablewt_examList->setItem(i, 0, new QTableWidgetItem(QString::number(pr.nId)));
+            ui->tablewt_examList->setItem(i, 1, new QTableWidgetItem(pr.sStartTime));
+            ui->tablewt_examList->setItem(i, 2, new QTableWidgetItem(pr.sEndTime));
+            ui->tablewt_examList->setItem(i, 3, new QTableWidgetItem(QString("%1:%2:%3")
+                                                                     .arg(pr.nUsedExamTime/1000/60/60, 2, 10, QChar('0'))
+                                                                     .arg(pr.nUsedExamTime/1000/60%60, 2, 10, QChar('0'))
+                                                                     .arg(pr.nUsedExamTime/1000%60, 2, 10, QChar('0'))));
+            ui->tablewt_examList->setItem(i, 4, new QTableWidgetItem(QString::number(pr.nTotalQuestionCount)));
+            ui->tablewt_examList->setItem(i, 5, new QTableWidgetItem(QString::number(pr.nSuccQuestionNum)));
+            ui->tablewt_examList->setItem(i, 6, new QTableWidgetItem(QString::number(pr.nFailQuestionNum)));
+            ui->tablewt_examList->setItem(i, 7, new QTableWidgetItem(QString::number(pr.nNotAnsweredCount)));
+            ui->tablewt_examList->setItem(i, 8, new QTableWidgetItem(QString::fromLocal8Bit("%1%").arg(pr.fObjectiveAccuracy)));
+
+            clOperation *clo = new clOperation(i, ui->tablewt_examList);
+            connect(clo, &clOperation::viewPracticePaper, this, [&](int nRow){
+                if(m_pReport == nullptr)
+                {
+                    m_pReport = std::make_shared<clopReport>(m_vPracticeRecordList[nRow], false, this);
+                    connect(m_pReport.get(), &clopReport::goback, this, [&](){
+                        m_pReport.reset();
+                    });
+					m_pReport->setUI(0, 0, width(), height());
+                }
+				m_pReport->show();
+            });
+            clo->setUI(ui->tablewt_examList->columnWidth(9), ui->tablewt_examList->rowHeight(i), CL_OPERATION_TYPE::cot_online_practice_result);
+            ui->tablewt_examList->setCellWidget(i, 9, clo);
+        }
+        m_vPracticeRecordList.swap(queryPracticeRecordList.vPracticeRecordList);
+    }
+    else
+    {
+        if(queryPracticeRecordList.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取练习列表信息失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(queryPracticeRecordList.sMessage, (QWidget*)(this->parent()));
+        }
+    }
+}
+
+void clopResultList::on_btn_clop_goback_clicked()
+{
+    emit goback();
+}

+ 33 - 0
client/clopResultList.h

@@ -0,0 +1,33 @@
+#ifndef CLOPRESULTLIST_H
+#define CLOPRESULTLIST_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+#include "clopReport.h"
+
+namespace Ui {
+class clopResultList;
+}
+
+class clopResultList : public QWidget
+{
+    Q_OBJECT
+signals:
+    void goback();
+public:
+    explicit clopResultList(CPracticeCourseInfo pci, QString sBatchName, QWidget *parent = nullptr);
+    ~clopResultList();
+
+    void setUI(const int nLeft, const int nTop, const int nWidth, const int nHeight);
+private slots:
+    void onQueryPracticeRecordList(CQueryPracticeRecordList queryPracticeRecordList);
+    void on_btn_clop_goback_clicked();
+
+private:
+    Ui::clopResultList *ui;
+
+    std::shared_ptr<clopReport> m_pReport;
+    std::vector<CPracticeRecord> m_vPracticeRecordList;
+};
+
+#endif // CLOPRESULTLIST_H

+ 117 - 0
client/clopResultList.ui

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>clopResultList</class>
+ <widget class="QWidget" name="clopResultList">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>910</width>
+    <height>611</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_clop_BG" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>871</width>
+     <height>571</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_clop_top" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>931</width>
+      <height>80</height>
+     </rect>
+    </property>
+    <property name="styleSheet">
+     <string notr="true"/>
+    </property>
+    <widget class="QLabel" name="label_clop_batch">
+     <property name="geometry">
+      <rect>
+       <x>40</x>
+       <y>30</y>
+       <width>81</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>批次:练习Test</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_clop_course">
+     <property name="geometry">
+      <rect>
+       <x>230</x>
+       <y>30</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>科目:测试试卷</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_clop_avgCorrectRate">
+     <property name="geometry">
+      <rect>
+       <x>370</x>
+       <y>30</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>平均正确率:16.67%</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_clop_maxCorrectRate">
+     <property name="geometry">
+      <rect>
+       <x>470</x>
+       <y>30</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>最高正确率:66.67%</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_clop_goback">
+     <property name="geometry">
+      <rect>
+       <x>700</x>
+       <y>30</y>
+       <width>75</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>返回列表</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QTableWidget" name="tablewt_examList">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>80</y>
+      <width>871</width>
+      <height>401</height>
+     </rect>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 1111 - 0
client/courseList.cpp

@@ -0,0 +1,1111 @@
+#include "courseList.h"
+#include "ui_courseList.h"
+
+
+#include "awMsgBox.h"
+#include <QDesktopWidget>
+#include <QMouseEvent>
+#include "CAppInfo.h"
+#include "CSqlite3DBProc.h"
+
+courseList::courseList(QWidget *parent) :
+	QWidget(parent),
+	ui(new Ui::courseList)
+{
+	ui->setupUi(this);
+
+	setWindowFlags(Qt::FramelessWindowHint);
+
+	setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	qRegisterMetaType<CGetStudentInfoBySession>("CGetStudentInfoBySession");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetStudentInfoBySession, this, &courseList::onGetStudentInfoBySession);
+	qRegisterMetaType<CSpecialtyNameList>("CSpecialtyNameList");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnSpecialtyNameList, this, &courseList::onSpecialtyNameList);
+	qRegisterMetaType<CCheckExamInProgress>("CCheckExamInProgress");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnCheckExamInProgress, this, &courseList::onCheckExamInProgress);
+	qRegisterMetaType<CGetStudentClientMenu>("CGetStudentClientMenu");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetStudentClientMenu, this, &courseList::onGetStudentClientMenu);
+	qRegisterMetaType<CGetUserNoticeList>("CGetUserNoticeList");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetUserNoticeList, this, &courseList::onGetUserNoticeList);	
+	qRegisterMetaType<CDownLoadFileInfo>("CDownLoadFileInfo");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnDownLoadFile, this, &courseList::onDownLoadFile);
+	qRegisterMetaType<CAppDownLoadUrl>("CAppDownLoadUrl");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnAppDownLoadUrl, this, &courseList::onAppDownLoadUrl);	
+	qRegisterMetaType<CStartExamLimit>("CStartExamLimit");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnStartExamLimit, this, &courseList::onStartExamLimit);
+	qRegisterMetaType<CIpLimit>("CIpLimit");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnIpLimit, this, &courseList::onIpLimit);
+	qRegisterMetaType<CGetExamProperty>("CGetExamProperty");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetExamProperty, this, &courseList::onGetExamProperty);
+	qRegisterMetaType<CFaceCheckEnabled>("CFaceCheckEnabled");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnFaceCheckEnabled, this, &courseList::onFaceCheckEnabled);
+	qRegisterMetaType<CLivenessEnabled>("CLivenessEnabled");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnLivenessEnabled, this, &courseList::onLivenessEnabled);
+
+	
+
+	sqlite3_stmt *stmt;
+	QString sSql = QString("select agreement from t_agreement where student_id=%1")
+		.arg(g_appInfoPtr->m_nStudentId);
+	if (!g_sysDBProcPtr->QuerySql(sSql.toStdString(), &stmt))
+	{
+		ShowMsg(QString::fromLocal8Bit("加载隐私协议信息失败!"), this);		
+	}
+
+	bool bAgreement = false;
+	int nField = 0;
+	g_sysDBProcPtr->BeginGetFields(stmt);
+	if (!g_sysDBProcPtr->Eof())
+	{
+		if (g_sysDBProcPtr->GetFields(0, nField, stmt) == 1)
+		{
+			bAgreement = true;
+		}
+	}
+	g_sysDBProcPtr->EndGetFields(stmt);
+
+	if (!bAgreement)
+	{
+		if (m_pPrivacyWidget == nullptr)
+		{
+			m_pPrivacyWidget = std::make_shared<privacyWidget>(this);
+			connect(m_pPrivacyWidget.get(), &privacyWidget::agreement, this, [&]() {
+				m_pPrivacyWidget.reset();
+				getStudentInfoBySession();
+			});
+			connect(m_pPrivacyWidget.get(), &privacyWidget::disAgreement, this, [&]() {
+				on_btn_exit_clicked();
+			});
+		}
+		m_pPrivacyWidget->show();
+		
+	}
+	else
+	{			
+		getStudentInfoBySession();
+	}
+
+    initUI();
+
+    m_pMobileLogin = nullptr;
+    m_pStudentInfo = nullptr;
+
+    m_pMobileLoginBtnTimer = std::make_shared<QTimer>();
+    m_pMobileLoginBtnTimer->setInterval(500);
+    connect(m_pMobileLoginBtnTimer.get(), &QTimer::timeout, this, &courseList::hideMobileLoginBtn);
+
+    m_pStudentInfoBtnTimer = std::make_shared<QTimer>();
+    m_pStudentInfoBtnTimer->setInterval(500);
+    connect(m_pStudentInfoBtnTimer.get(), &QTimer::timeout, this, &courseList::hideStudentInfoBtn);
+
+	m_pRefreshTimer = std::make_shared<QTimer>();
+	m_pRefreshTimer->setInterval(1000*60*3);
+	connect(m_pRefreshTimer.get(), &QTimer::timeout, this, []() {
+		CHttpRequestPackage hrp;
+		hrp.sUri = "/api/ecs_core/rolePrivilege/getStudentClientMenu";
+		hrp.nRequestType = RequestType::rtGetStudentClientMenu;
+		hrp.sParamList.push_back(QString::fromLocal8Bit("rootOrgId,%1").arg(g_appInfoPtr->m_sRootOrgId));
+		g_httpBllPtr->get(hrp);
+
+		CHttpRequestPackage hrp1;
+		hrp1.sUri = "/api/ecs_core/student/online_signal/" + QString::number(g_appInfoPtr->m_nStudentId); 
+		hrp1.nRequestType = RequestType::rtOnlineSignal;		
+		g_httpBllPtr->get(hrp1);
+	});
+
+    ui->btn_mobileLogin->installEventFilter(this);
+    ui->btn_studentInfo->installEventFilter(this);
+}
+
+courseList::~courseList()
+{
+    delete ui;
+}
+
+void courseList::initUI()
+{
+    QDesktopWidget *dekwiget = QApplication::desktop();
+    setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+    ui->widget_cl_BG->setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+    ui->widget_menu->setGeometry(0, 0, g_appInfoPtr->m_fRate*96, height());
+    ui->widget_top->setGeometry(ui->widget_menu->width(), 0, width() - ui->widget_menu->width(), g_appInfoPtr->m_fRate*56);
+    ui->widget_bottom->setGeometry(ui->widget_top->x(), height() - g_appInfoPtr->m_fRate*38, ui->widget_top->width(), g_appInfoPtr->m_fRate*38);
+    ui->stw_courseList->setGeometry(ui->widget_top->x(), ui->widget_top->y() + ui->widget_top->height(),
+                                    ui->widget_top->width(), height() - ui->widget_top->height() - ui->widget_bottom->height());
+    /*
+	clOnlineExam *cloe = new clOnlineExam;
+    cloe->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+    ui->stw_courseList->addWidget(cloe);
+
+    clOnlineHomework *clow = new clOnlineHomework;
+    clow->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+    ui->stw_courseList->addWidget(clow);
+
+    clOnlinePractice *clop = new clOnlinePractice;
+    clop->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+    ui->stw_courseList->addWidget(clop);
+
+    clOfflineExam *cloffe = new clOfflineExam;
+    cloffe->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+    ui->stw_courseList->addWidget(cloffe);
+
+    clNoticeList *clnl = new clNoticeList;
+    clnl->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+    ui->stw_courseList->addWidget(clnl);
+
+    clEditPassword *clep = new clEditPassword;
+    clep->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+    ui->stw_courseList->addWidget(clep);
+	ui->stw_courseList->setCurrentIndex(0);
+	*/
+    
+    ui->label_cl_org_icon->setGeometry(g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*36, g_appInfoPtr->m_fRate*36);
+    ui->btn_onlineExam->setGeometry(g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*103, g_appInfoPtr->m_fRate*56, g_appInfoPtr->m_fRate*66);
+    ui->btn_onlineHomework->setGeometry(ui->btn_onlineExam->x(), ui->btn_onlineExam->y() + ui->btn_onlineExam->height() + g_appInfoPtr->m_fRate*17,
+                                        ui->btn_onlineExam->width(), ui->btn_onlineExam->height());
+    ui->btn_onlinePractice->setGeometry(ui->btn_onlineExam->x(), ui->btn_onlineHomework->y() + ui->btn_onlineHomework->height() + g_appInfoPtr->m_fRate*17,
+                                        ui->btn_onlineExam->width(), ui->btn_onlineExam->height());
+    ui->btn_offlineExam->setGeometry(ui->btn_onlineExam->x(), ui->btn_onlinePractice->y() + ui->btn_onlinePractice->height() + g_appInfoPtr->m_fRate*17,
+                                        ui->btn_onlineExam->width(), ui->btn_onlineExam->height());
+    ui->btn_notice->setGeometry(ui->btn_onlineExam->x(), ui->btn_offlineExam->y() + ui->btn_offlineExam->height() + g_appInfoPtr->m_fRate*17,
+                                        ui->btn_onlineExam->width(), ui->btn_onlineExam->height());
+    ui->btn_noticeNum->setGeometry(ui->btn_notice->x() + ui->btn_notice->width() - g_appInfoPtr->m_fRate*16, ui->btn_notice->y(),
+                                   g_appInfoPtr->m_fRate*16, g_appInfoPtr->m_fRate*16);
+	ui->btn_noticeNum->setVisible(false);
+	ui->btn_editPassword->setGeometry(ui->btn_onlineExam->x(), ui->btn_notice->y() + ui->btn_notice->height() + g_appInfoPtr->m_fRate*17,
+                                        ui->btn_onlineExam->width(), ui->btn_onlineExam->height());
+    ui->label_currentPlaceIcon->setGeometry(g_appInfoPtr->m_fRate*30, (ui->widget_top->height() - g_appInfoPtr->m_fRate*10)/2,
+                                       g_appInfoPtr->m_fRate*10, g_appInfoPtr->m_fRate*10);
+    ui->label_currrentPlace->adjustSize();
+    ui->label_currrentPlace->setGeometry(ui->label_currentPlaceIcon->x() + ui->label_currentPlaceIcon->width() + g_appInfoPtr->m_fRate*10,
+                                         (ui->widget_top->height() - ui->label_currrentPlace->height())/2, g_appInfoPtr->m_fRate*400, ui->label_currrentPlace->height());
+    ui->btn_cl_close->setGeometry(ui->widget_top->width() - g_appInfoPtr->m_fRate*10 - g_appInfoPtr->m_fRate*24, g_appInfoPtr->m_fRate*16,
+                                  g_appInfoPtr->m_fRate*24, g_appInfoPtr->m_fRate*24);
+    ui->btn_minisize->setGeometry(ui->btn_cl_close->x() - g_appInfoPtr->m_fRate*6 - ui->btn_cl_close->width(), ui->btn_cl_close->y(),
+                                  ui->btn_cl_close->width(), ui->btn_cl_close->height());
+    ui->label_VLine->setGeometry(ui->btn_minisize->x() - g_appInfoPtr->m_fRate*21, ui->btn_cl_close->y(),
+                                 g_appInfoPtr->m_fRate*1, g_appInfoPtr->m_fRate*24);
+    ui->btn_exit->setGeometry(ui->label_VLine->x() - g_appInfoPtr->m_fRate*(20+24), ui->btn_cl_close->y(),
+                              ui->btn_cl_close->width(), ui->btn_cl_close->height());
+    ui->btn_studentInfo->setGeometry(ui->btn_exit->x() - g_appInfoPtr->m_fRate*(10+90), ui->btn_cl_close->y(),
+                                     g_appInfoPtr->m_fRate*90, g_appInfoPtr->m_fRate*24);
+    ui->btn_studentInfo->setIconSize(QSize(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+    ui->btn_studentInfo->setIcon(QIcon(":/images/icon-student-info.png"));
+    ui->label_arrow->setGeometry(ui->btn_studentInfo->x() + (ui->btn_studentInfo->width() - g_appInfoPtr->m_fRate*12)/2,
+                                 ui->btn_studentInfo->y() + ui->btn_studentInfo->height(),
+                                 g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*3);
+    ui->label_arrow->setVisible(false);
+    ui->btn_mobileLogin->setGeometry(ui->btn_studentInfo->x() - g_appInfoPtr->m_fRate*(10+167), ui->btn_cl_close->y(),
+                                     g_appInfoPtr->m_fRate*167, g_appInfoPtr->m_fRate*24);
+    ui->btn_mobileLogin->setIconSize(QSize(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+    ui->btn_mobileLogin->setIcon(QIcon(":/images/icon-mobile-login.png"));
+    ui->label_cl_company->adjustSize();
+    ui->label_cl_company->setGeometry(g_appInfoPtr->m_fRate*30, (ui->widget_bottom->height() - ui->label_cl_company->height())/2,
+                                      ui->label_cl_company->width(), ui->label_cl_company->height());
+    ui->label_cl_version->adjustSize();
+    ui->label_cl_version->setGeometry(ui->widget_bottom->width() - g_appInfoPtr->m_fRate*30 - ui->label_cl_version->width(),
+                                      ui->label_cl_company->y(), ui->label_cl_version->width(), ui->label_cl_version->height());
+    ui->label_serverTime->adjustSize();
+    ui->label_serverTime->setGeometry(ui->label_cl_version->x() - g_appInfoPtr->m_fRate*20 - ui->label_serverTime->width(), ui->label_cl_company->y(),
+                                      ui->label_serverTime->width(), ui->label_serverTime->height());
+
+    setCheck(COURSE_MENU_BTN_TYPE::cmbt_online_exam);
+
+}
+
+void courseList::menuBtnRePosistion()
+{
+	int nTop = g_appInfoPtr->m_fRate * 103;
+	if(ui->btn_onlineExam->isVisible())
+	{ 
+		ui->btn_onlineExam->setGeometry(g_appInfoPtr->m_fRate * 20, nTop, g_appInfoPtr->m_fRate * 56, g_appInfoPtr->m_fRate * 66);
+		nTop = nTop + ui->btn_onlineExam->height() + g_appInfoPtr->m_fRate * 17;
+	}
+	
+	if(ui->btn_onlineHomework->isVisible())
+	{ 
+		ui->btn_onlineHomework->setGeometry(ui->btn_onlineExam->x(), nTop,
+			ui->btn_onlineExam->width(), ui->btn_onlineExam->height());
+		nTop = nTop + ui->btn_onlineExam->height() + g_appInfoPtr->m_fRate * 17;
+	}
+	
+	if (ui->btn_onlinePractice->isVisible())
+	{
+		ui->btn_onlinePractice->setGeometry(ui->btn_onlineExam->x(), nTop,
+			ui->btn_onlineExam->width(), ui->btn_onlineExam->height());
+		nTop = nTop + ui->btn_onlineExam->height() + g_appInfoPtr->m_fRate * 17;
+	}
+	
+	if (ui->btn_offlineExam->isVisible())
+	{
+		ui->btn_offlineExam->setGeometry(ui->btn_onlineExam->x(), nTop,
+			ui->btn_onlineExam->width(), ui->btn_onlineExam->height());
+		nTop = nTop + ui->btn_onlineExam->height() + g_appInfoPtr->m_fRate * 17;
+	}
+
+	if (ui->btn_notice->isVisible())
+	{
+		ui->btn_notice->setGeometry(ui->btn_onlineExam->x(), nTop,
+			ui->btn_onlineExam->width(), ui->btn_onlineExam->height());
+		ui->btn_noticeNum->setGeometry(ui->btn_notice->x() + ui->btn_notice->width() - g_appInfoPtr->m_fRate * 16, ui->btn_notice->y(),
+			g_appInfoPtr->m_fRate * 16, g_appInfoPtr->m_fRate * 16);
+		nTop = nTop + ui->btn_onlineExam->height() + g_appInfoPtr->m_fRate * 17;
+	}
+	
+	if (ui->btn_editPassword->isVisible())
+	{
+		ui->btn_editPassword->setGeometry(ui->btn_onlineExam->x(), ui->btn_notice->y() + ui->btn_notice->height() + g_appInfoPtr->m_fRate * 17,
+			ui->btn_onlineExam->width(), ui->btn_onlineExam->height());
+	}
+}
+
+void courseList::on_btn_minisize_clicked()
+{
+    emit minisize();
+}
+
+void courseList::on_btn_cl_close_clicked()
+{
+    close();
+	logout();
+}
+
+void courseList::setCheck(COURSE_MENU_BTN_TYPE cmbt)
+{
+    QString sNormalSheet = QString(R"(outline:none;
+                                   font-size:%1px;
+                                   font-family:"Microsoft YaHei";
+                                   font-weight:400;
+                                   color:rgba(255,255,255,0.4);
+                                   background:rgba(255,255,255,0);
+                                   border-radius:%2;)").arg((int)(g_appInfoPtr->m_fRate*12)).arg(g_appInfoPtr->m_fRate*12);
+    QString sCheckSheet = QString(R"(QPushButton
+                                  {
+                                      outline:none;
+                                      font-size:%1px;
+                                      font-family:"Microsoft YaHei";
+                                      font-weight:400;
+                                      color:rgba(255,255,255,1);
+                                      border-radius:%2px;
+                                      background:qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(16, 195, 182, 1), stop:1 rgba(39, 217, 146, 1));
+                                  })").arg((int)(g_appInfoPtr->m_fRate*12)).arg(g_appInfoPtr->m_fRate*12);
+    ui->btn_onlineExam->setStyleSheet(sNormalSheet);
+    ui->btn_onlineHomework->setStyleSheet(sNormalSheet);
+    ui->btn_onlinePractice->setStyleSheet(sNormalSheet);
+    ui->btn_offlineExam->setStyleSheet(sNormalSheet);
+    ui->btn_notice->setStyleSheet(sNormalSheet);
+    ui->btn_editPassword->setStyleSheet(sNormalSheet);
+
+	if (m_pOnlineExam != nullptr)
+	{
+		m_pOnlineExam->hide();
+	}
+
+	if (m_pOnlineHomework != nullptr)
+	{
+		m_pOnlineHomework->hide();
+	}
+	
+	if (m_pOnlinePractice != nullptr)
+	{
+		m_pOnlinePractice->hide();
+	}
+	
+	if (m_pOfflineExam != nullptr)
+	{
+		m_pOfflineExam->hide();
+	}
+	
+	if (m_pNoticeList != nullptr)
+	{
+		m_pNoticeList->hide();
+	}
+
+	if (m_pEditPassword != nullptr)
+	{
+		m_pEditPassword->hide();
+	}
+
+    QString sCurrentPlace = "";
+	if (cmbt == COURSE_MENU_BTN_TYPE::cmbt_online_exam)
+	{
+		if (m_pOnlineExam == nullptr)
+		{		
+			m_pOnlineExam = std::make_shared<clOnlineExam>(ui->stw_courseList);
+			m_pOnlineExam->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+            connect(m_pOnlineExam.get(), &clOnlineExam::enterExam, this, &courseList::onEnterExam);
+		}
+		m_pOnlineExam->show();
+
+		ui->btn_onlineExam->setStyleSheet(sCheckSheet);
+        sCurrentPlace = QString::fromLocal8Bit("当前所在位置:在线考试");
+    }
+    else if(cmbt == COURSE_MENU_BTN_TYPE::cmbt_online_homework)
+    {
+		if (m_pOnlineHomework == nullptr)
+		{
+			m_pOnlineHomework = std::make_shared<clOnlineHomework>(ui->stw_courseList);
+			m_pOnlineHomework->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());		
+            connect(m_pOnlineHomework.get(), &clOnlineHomework::enterExam, this, &courseList::onEnterExam);
+		}
+		m_pOnlineHomework->show();
+
+        ui->btn_onlineHomework->setStyleSheet(sCheckSheet);
+        sCurrentPlace = QString::fromLocal8Bit("当前所在位置:在线作业");
+    }
+    else if(cmbt == COURSE_MENU_BTN_TYPE::cmbt_online_practice)
+    {
+		if (m_pOnlinePractice == nullptr)
+		{
+			m_pOnlinePractice = std::make_shared<clOnlinePractice>(ui->stw_courseList);
+			m_pOnlinePractice->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+            connect(m_pOnlinePractice.get(), &clOnlinePractice::enterExam, this, &courseList::onEnterExam);
+		}
+		m_pOnlinePractice->show();
+
+        ui->btn_onlinePractice->setStyleSheet(sCheckSheet);
+        sCurrentPlace = QString::fromLocal8Bit("当前所在位置:在线练习");
+    }
+    else if(cmbt == COURSE_MENU_BTN_TYPE::cmbt_offline_exam)
+    {
+		if (m_pOfflineExam == nullptr)
+		{
+			m_pOfflineExam = std::make_shared<clOfflineExam>(ui->stw_courseList);
+			m_pOfflineExam->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+		}
+		m_pOfflineExam->show();
+
+        ui->btn_offlineExam->setStyleSheet(sCheckSheet);
+        sCurrentPlace = QString::fromLocal8Bit("当前所在位置:离线考试");
+    }
+    else if(cmbt == COURSE_MENU_BTN_TYPE::cmbt_notice)
+    {
+		if (m_pNoticeList == nullptr)
+		{
+			m_pNoticeList = std::make_shared<clNoticeList>(ui->stw_courseList);
+			m_pNoticeList->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+		}
+		m_pNoticeList->show();
+
+        ui->btn_notice->setStyleSheet(sCheckSheet);
+        sCurrentPlace = QString::fromLocal8Bit("当前所在位置:公告通知");
+    }
+    else if(cmbt == COURSE_MENU_BTN_TYPE::cmbt_edit_password)
+    {
+		if (m_pEditPassword == nullptr)
+		{
+			m_pEditPassword = std::make_shared<clEditPassword>(ui->stw_courseList);
+			m_pEditPassword->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+		}
+		m_pEditPassword->show();
+
+        ui->btn_editPassword->setStyleSheet(sCheckSheet);
+        sCurrentPlace = QString::fromLocal8Bit("当前所在位置:修改密码");
+    }
+
+    ui->label_currrentPlace->setText(sCurrentPlace);
+}
+
+void courseList::onEnterExam(CL_OPERATION_TYPE cot, __int64 nExamId, __int64 nExamStudentId)
+{
+    if(cot == CL_OPERATION_TYPE::cot_online_exam)
+    {
+        g_appInfoPtr->m_oExamInfo.sExamType = EXAM_TYPES::ONLINE;
+    }
+    g_appInfoPtr->m_oExamInfo.nExamId = nExamId;
+	g_appInfoPtr->m_oExamInfo.nExamStudentId = nExamStudentId;
+    //开考限流
+    CHttpRequestPackage hrp;
+    hrp.sUri = QString("https://tcc.qmth.com.cn/rate_limit/prod/startExam/%1").arg(100);
+    hrp.nRequestType = RequestType::rtStartExamLimit;
+    g_httpBllPtr->getUrl(hrp);
+}
+
+void courseList::onStartExamLimit(CStartExamLimit startExamLimit)
+{
+	if (startExamLimit.nCode == 200)
+	{
+		if (startExamLimit.bPass)
+		{
+            CHttpRequestPackage hrp;
+            hrp.sUri = QString("/api/ecs_exam_work/exam/ipLimit/%1").arg(g_appInfoPtr->m_oExamInfo.nExamId);
+            hrp.nRequestType = RequestType::rtIpLimit;
+            g_httpBllPtr->get(hrp);
+		}
+		else
+		{
+			//重试
+		}
+	}
+	else
+	{
+		ShowMsg(QString::fromLocal8Bit("开考失败"), this);
+	}
+}
+
+void courseList::onIpLimit(CIpLimit ipLimit)
+{
+	if (ipLimit.nCode == 200)
+	{
+		if (!ipLimit.bLimited)
+		{
+            //断点
+            CHttpRequestPackage hrp;
+            hrp.sUri = "/api/ecs_oe_student/client/exam/process/checkExamInProgress";
+            hrp.nRequestType = RequestType::rtCheckExamInProgress;
+            hrp.sCommonStr = EXAM_INPROGRESS_ENTER_TYPE::EIET_ENTER_EXAM;
+            g_httpBllPtr->post(hrp);
+
+		}
+		else
+		{
+			ShowMsg(QString::fromLocal8Bit("当前ip限制参加考试"), this);
+		}
+	}
+	else
+	{
+		ShowMsg(QString::fromLocal8Bit("开考失败"), this);
+	}
+}
+
+void courseList::onGetExamProperty(CGetExamProperty getExamProperty)
+{
+	if (getExamProperty.nCode == 200)
+	{
+        if (getExamProperty.sType == "CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE")
+		{
+            g_appInfoPtr->m_oExamInfo.bIsFaceCheck = getExamProperty.bIsFaceCheck;
+            g_appInfoPtr->m_oExamInfo.nWarnThreshold = getExamProperty.nWarnThreshold;
+            g_appInfoPtr->m_oExamInfo.nSnapshotInterval = getExamProperty.nSnapshotInterval;
+            g_appInfoPtr->m_oExamInfo.nFaceVerifyStartMinute = getExamProperty.nFaceVerifyStartMinute;
+            g_appInfoPtr->m_oExamInfo.nFaceVerifyEndMinute = getExamProperty.nFaceVerifyEndMinute;
+            g_appInfoPtr->m_oExamInfo.bIsStrangerEnable = getExamProperty.bIsStrangerEnable;
+            if (getExamProperty.bCheckEnvironment && !g_appInfoPtr->m_oExamInfo.bIsExamInProgress)
+			{
+                if(m_pWhetherEnvTest == nullptr)
+                {
+                    m_pWhetherEnvTest = std::make_shared<etWhetherEnvTest>(this);
+                    connect(m_pWhetherEnvTest.get(), &etWhetherEnvTest::envTest, this, [&](){
+                        m_pWhetherEnvTest.reset();
+                        //环境检测
+                        if (m_pEnvironmentalTest == nullptr)
+                        {
+                            m_pEnvironmentalTest = std::make_shared<environmentalTest>(this);
+                            connect(m_pEnvironmentalTest.get(), &environmentalTest::enterExam, this, [&](){
+                                m_pEnvironmentalTest.reset();
+                                //人脸识别
+                                CHttpRequestPackage hrp;
+                                hrp.sUri = QString("/api/ecs_exam_work/exam/faceCheckEnabled/%1")
+                                    .arg(g_appInfoPtr->m_oExamInfo.nExamId);
+                                hrp.nRequestType = RequestType::rtFaceCheckEnabled;
+                                g_httpBllPtr->get(hrp);
+                            });
+                        }
+                        m_pEnvironmentalTest->show();
+                    });
+
+                    connect(m_pWhetherEnvTest.get(), &etWhetherEnvTest::skipEnvTest, this, [&](){
+                        m_pWhetherEnvTest.reset();
+                        //人脸识别
+                        CHttpRequestPackage hrp;
+                        hrp.sUri = QString("/api/ecs_exam_work/exam/faceCheckEnabled/%1")
+                            .arg(g_appInfoPtr->m_oExamInfo.nExamId);
+                        hrp.nRequestType = RequestType::rtFaceCheckEnabled;
+                        g_httpBllPtr->get(hrp);
+                    });
+                }
+                m_pWhetherEnvTest->show();
+
+			}
+			else
+			{
+				//人脸识别
+				CHttpRequestPackage hrp;
+				hrp.sUri = QString("/api/ecs_exam_work/exam/faceCheckEnabled/%1")
+					.arg(g_appInfoPtr->m_oExamInfo.nExamId);
+				hrp.nRequestType = RequestType::rtFaceCheckEnabled;
+				g_httpBllPtr->get(hrp);
+			}
+		}
+	}
+	else
+	{
+        if(getExamProperty.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取考试信息失败"), this);
+        }
+        else
+        {
+            ShowMsg(getExamProperty.sMessage, this);
+        }
+	}
+}
+
+void courseList::onFaceCheckEnabled(CFaceCheckEnabled faceCheckEnabled)
+{
+	if (faceCheckEnabled.nCode == 200)
+	{
+        //人脸检测
+        g_appInfoPtr->m_oExamInfo.bFaceCheck = faceCheckEnabled.bEnabled;
+        if (faceCheckEnabled.bEnabled && !g_appInfoPtr->m_oExamInfo.bIsExamInProgress)
+		{			         
+			if (m_pFaceCompare == nullptr)
+			{
+				m_pFaceCompare = std::make_shared<faceCompare>(this);
+				connect(m_pFaceCompare.get(), &faceCompare::exitFaceCompare, this, [&]() {
+					m_pFaceCompare.reset();
+				});
+				connect(m_pFaceCompare.get(), &faceCompare::faceComparePass, this, [&]() {
+					m_pFaceCompare.reset();
+                    //活体检测
+                    CHttpRequestPackage hrp;
+                    hrp.sUri = QString("/api/ecs_exam_work/exam/identificationOfLivingEnabled/%1")
+                        .arg(g_appInfoPtr->m_oExamInfo.nExamId);
+                    hrp.nRequestType = RequestType::rtLivenessEnabled;
+                    g_httpBllPtr->get(hrp);
+				});
+
+			}
+			m_pFaceCompare->show();
+		}
+		else
+		{
+			//活体检测
+			CHttpRequestPackage hrp;
+			hrp.sUri = QString("/api/ecs_exam_work/exam/identificationOfLivingEnabled/%1")
+				.arg(g_appInfoPtr->m_oExamInfo.nExamId);
+			hrp.nRequestType = RequestType::rtLivenessEnabled;
+            g_httpBllPtr->get(hrp);
+		}
+	}
+	else
+	{
+        if(faceCheckEnabled.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取人脸识别信息失败"), this);
+        }
+        else
+        {
+            ShowMsg(faceCheckEnabled.sMessage, this);
+        }
+	}
+}
+
+void courseList::onLivenessEnabled(CLivenessEnabled livenessEnabled)
+{
+	if (livenessEnabled.nCode == 200)
+	{
+        g_appInfoPtr->m_oExamInfo.bLivenessCheck = livenessEnabled.bEnabled;
+
+        //进入待考
+        enterWaitExam();
+	}
+	else
+	{
+        if(livenessEnabled.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取活体信息失败"), this);
+        }
+        else
+        {
+            ShowMsg(livenessEnabled.sMessage, this);
+        }
+	}
+}
+
+void courseList::enterWaitExam()
+{
+	//进入待考
+	if (m_pBackground == nullptr)
+	{
+		if (g_appInfoPtr->m_oExamInfo.bIsExamInProgress)
+		{
+			m_pBackground = std::make_shared<awBackground>(AW_WIDGET_TYPE::awwt_answerWidget, this);           
+			m_pResumeExam.reset();
+		}
+		else
+		{
+			m_pBackground = std::make_shared<awBackground>(AW_WIDGET_TYPE::awwt_waitExam, this);
+
+		}
+		
+        connect(m_pBackground.get(), &awBackground::closeWidget, this, [&](){
+            m_pBackground.reset();
+        });
+        connect(m_pBackground.get(), &awBackground::showPracticeInfo, this, [&](){
+            m_pBackground.reset();
+            CPracticeRecord pr;
+            pr.nId = g_appInfoPtr->m_oExamInfo.nExamRecordDataId;
+            pr.sCourseCode = g_appInfoPtr->m_oExamInfo.sCourseCode;
+            pr.sCourseName = g_appInfoPtr->m_oExamInfo.sCourseName;
+            if(m_pClopReport == nullptr)
+            {
+                m_pClopReport = std::make_shared<clopReport>(pr, true, ui->stw_courseList);
+                connect(m_pClopReport.get(), &clopReport::goback, this, [&](){
+                    m_pClopReport.reset();
+                });
+                m_pClopReport->setUI(0, 0, ui->stw_courseList->width(), ui->stw_courseList->height());
+            }
+            m_pClopReport->show();
+        });
+	}
+	m_pBackground->show();
+}
+
+void courseList::on_btn_onlineExam_clicked()
+{
+    ui->stw_courseList->setCurrentIndex(0);
+    setCheck(COURSE_MENU_BTN_TYPE::cmbt_online_exam);
+}
+
+void courseList::on_btn_onlineHomework_clicked()
+{
+    ui->stw_courseList->setCurrentIndex(1);
+    setCheck(COURSE_MENU_BTN_TYPE::cmbt_online_homework);
+}
+
+void courseList::on_btn_onlinePractice_clicked()
+{
+    ui->stw_courseList->setCurrentIndex(2);
+    setCheck(COURSE_MENU_BTN_TYPE::cmbt_online_practice);
+}
+
+void courseList::on_btn_offlineExam_clicked()
+{
+    ui->stw_courseList->setCurrentIndex(3);
+    setCheck(COURSE_MENU_BTN_TYPE::cmbt_offline_exam);
+}
+
+void courseList::on_btn_notice_clicked()
+{
+    ui->stw_courseList->setCurrentIndex(4);
+    setCheck(COURSE_MENU_BTN_TYPE::cmbt_notice);
+}
+
+void courseList::on_btn_editPassword_clicked()
+{
+    ui->stw_courseList->setCurrentIndex(5);
+    setCheck(COURSE_MENU_BTN_TYPE::cmbt_edit_password);
+}
+
+void courseList::on_btn_exit_clicked()
+{
+    close();
+	logout();
+}
+
+void courseList::logout()
+{
+	CHttpRequestPackage hrp;
+	hrp.sUri = "/api/ecs_core/auth/logout";
+	hrp.nRequestType = RequestType::rtLogout;
+	g_httpBllPtr->get(hrp);
+}
+bool courseList::eventFilter(QObject *obj, QEvent *ev)
+{
+    if(ev->type() == QEvent::Enter)
+    {
+        if(obj == ui->btn_mobileLogin)
+        {
+            m_pMobileLoginBtnTimer->stop();
+            if(m_pStudentInfo != nullptr)
+            {
+                m_pStudentInfo.reset();
+                m_pStudentInfo = nullptr;
+            }
+            if(m_pMobileLogin == nullptr)
+            {
+                m_pMobileLogin = std::make_shared<clMobileLogin>(this);
+                m_pMobileLogin->setUI(g_appInfoPtr->m_fRate*838, g_appInfoPtr->m_fRate*53, g_appInfoPtr->m_fRate*240, g_appInfoPtr->m_fRate*290);
+                m_pMobileLogin->show();
+                m_pMobileLogin->installEventFilter(this);
+            }
+			ui->btn_mobileLogin->setIcon(QIcon(":/images/icon-mobile-login-hover.png"));
+        }
+        else if(obj == ui->btn_studentInfo)
+        {
+            m_pStudentInfoBtnTimer->stop();
+            if(m_pMobileLogin != nullptr)
+            {
+                m_pMobileLogin.reset();
+                m_pMobileLogin = nullptr;
+            }
+            if(m_pStudentInfo == nullptr)
+            {
+                m_pStudentInfo = std::make_shared<clStudentInfo>(this);
+				connect(m_pStudentInfo.get(), &clStudentInfo::sgnStudentLogout, this, [&]() {
+					on_btn_exit_clicked();
+				});
+				connect(m_pStudentInfo.get(), &clStudentInfo::sgnStudentEditPassword, this, [&]() {
+					if (m_pStudentInfo != nullptr)
+					{
+						m_pStudentInfo.reset();
+					}
+					on_btn_editPassword_clicked(); 
+				});
+                m_pStudentInfo->setUI(g_appInfoPtr->m_fRate*838, g_appInfoPtr->m_fRate*53, g_appInfoPtr->m_fRate*330, g_appInfoPtr->m_fRate*240);
+                m_pStudentInfo->show();
+                m_pStudentInfo->installEventFilter(this);
+            }
+			ui->btn_studentInfo->setIcon(QIcon(":/images/icon-student-info-hover.png"));
+        }
+        else if(obj == m_pMobileLogin.get())
+        {
+            m_pMobileLoginBtnTimer->stop();
+        }
+        else if(obj == m_pStudentInfo.get())
+        {
+            m_pStudentInfoBtnTimer->stop();
+        }
+    }
+    else if(ev->type() == QEvent::Leave)
+    {
+        if(obj == ui->btn_mobileLogin || obj == m_pMobileLogin.get())
+        {
+			ui->btn_mobileLogin->setIcon(QIcon(":/images/icon-mobile-login.png"));
+            m_pMobileLoginBtnTimer->start();
+        }
+
+        if(obj == ui->btn_studentInfo || obj == m_pStudentInfo.get())
+        {
+			ui->btn_studentInfo->setIcon(QIcon(":/images/icon-student-info.png"));
+            m_pStudentInfoBtnTimer->start();
+        }
+    }
+
+    return QWidget::eventFilter(obj, ev);
+}
+
+void courseList::hideStudentInfoBtn()
+{
+    if(m_pStudentInfo != nullptr)
+    {
+        m_pStudentInfo.reset();
+    }	
+}
+
+void courseList::hideMobileLoginBtn()
+{
+    if(m_pMobileLogin != nullptr)
+    {
+        m_pMobileLogin.reset();
+    }
+	
+}
+
+void courseList::getStudentInfoBySession()
+{
+	CHttpRequestPackage hrp;
+	hrp.sUri = "/api/ecs_core/student/getStudentInfoBySession";
+	hrp.nRequestType = RequestType::rtGetStudentInfoBySession;
+	g_httpBllPtr->get(hrp);
+}
+
+
+
+void courseList::onGetStudentInfoBySession(CGetStudentInfoBySession getStudentInfoBySession)
+{
+	if (getStudentInfoBySession.nCode == 200)
+	{
+		g_appInfoPtr->m_nStudentId = getStudentInfoBySession.nId;
+		g_appInfoPtr->m_sStudentName = getStudentInfoBySession.sName;
+		g_appInfoPtr->m_nOrgId = getStudentInfoBySession.nOrgId;
+		g_appInfoPtr->m_sOrgCode = getStudentInfoBySession.sOrgCode;
+		g_appInfoPtr->m_sOrgName = getStudentInfoBySession.sOrgName;
+		g_appInfoPtr->m_sStudentCode = getStudentInfoBySession.sStudentCode;
+		g_appInfoPtr->m_sStudentIdentityNumber = getStudentInfoBySession.sIdentityNumber;
+		g_appInfoPtr->m_sStudentPhotoPath = getStudentInfoBySession.sPhotoPath;
+		g_appInfoPtr->m_bStudentEnable = getStudentInfoBySession.bEnable;
+
+		ui->btn_studentInfo->setText(g_appInfoPtr->m_sStudentName);
+		ui->btn_studentInfo->setVisible(g_appInfoPtr->m_bStudentEnable);
+		ui->btn_mobileLogin->setVisible(g_appInfoPtr->m_bShowStudentClientAppQrcode);
+
+		CHttpRequestPackage hrp;
+		hrp.sUri = "/api/ecs_exam_work/exam_student/specialtyNameList";
+		hrp.nRequestType = RequestType::rtSpecialtyNameList;
+		g_httpBllPtr->get(hrp);
+
+		if (!g_appInfoPtr->m_sStudentPhotoPath.isEmpty())
+		{
+			QString sFileName = g_appInfoPtr->m_sStudentPhotoPath.right(g_appInfoPtr->m_sStudentPhotoPath.length() - g_appInfoPtr->m_sStudentPhotoPath.lastIndexOf("/") - 1);
+			sFileName = g_appInfoPtr->m_sCacheFileDir + sFileName;
+			CHttpRequestPackage hrp;
+			hrp.sUri = g_appInfoPtr->m_sStudentPhotoPath;
+			hrp.sCommonStr = sFileName;
+			hrp.nRequestType = RequestType::rtDownLoadFile;
+
+			g_httpBllPtr->downLoad(hrp);
+		}
+
+		if (g_appInfoPtr->m_bShowStudentClientAppQrcode)
+		{
+			CHttpRequestPackage hrp;
+			hrp.sUri = "/api/ecs_core/systemProperty/APP_DOWNLOAD_URL";
+			hrp.nRequestType = RequestType::rtAppDownLoadUrl;
+			g_httpBllPtr->get(hrp);
+		}
+	}
+	else
+	{
+        if(getStudentInfoBySession.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取考生信息失败"), this);
+        }
+        else
+        {
+            ShowMsg(getStudentInfoBySession.sMessage, this);
+        }
+	}
+}
+
+void courseList::onAppDownLoadUrl(CAppDownLoadUrl appDownLoadUrl)
+{
+	if (appDownLoadUrl.nCode == 200)
+	{
+		g_appInfoPtr->m_sAppDownLoadUrl = appDownLoadUrl.sUrl;
+	}
+    else
+    {
+        if(appDownLoadUrl.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取APP下载地址失败"), this);
+        }
+        else
+        {
+            ShowMsg(appDownLoadUrl.sMessage, this);
+        }
+    }
+}
+
+void courseList::onDownLoadFile(CDownLoadFileInfo downLoadFileInfo)
+{
+	QString sFileName = g_appInfoPtr->m_sStudentPhotoPath.right(g_appInfoPtr->m_sStudentPhotoPath.length() - g_appInfoPtr->m_sStudentPhotoPath.lastIndexOf("/") - 1);
+	sFileName = g_appInfoPtr->m_sCacheFileDir + sFileName;
+	if (downLoadFileInfo.sFileName == sFileName)
+	{
+		if (downLoadFileInfo.nCode == 200)
+		{
+		
+		}
+		else
+		{
+            if(downLoadFileInfo.sMessage.isEmpty())
+            {
+                ShowMsg(QString::fromLocal8Bit("下载失败"), this);
+            }
+            else
+            {
+                ShowMsg(downLoadFileInfo.sMessage, this);
+            }
+		}
+	}
+}
+
+void courseList::onSpecialtyNameList(CSpecialtyNameList specialtyNameList)
+{
+	if (specialtyNameList.nCode == 200)
+	{
+		g_appInfoPtr->m_sSpecialtyName = specialtyNameList.sSpecialtyName;
+		CHttpRequestPackage hrp;
+		hrp.sUri = "/api/ecs_oe_student/client/exam/process/checkExamInProgress";
+		hrp.nRequestType = RequestType::rtCheckExamInProgress;
+		hrp.sCommonStr = EXAM_INPROGRESS_ENTER_TYPE::EIET_COURSE_LIST;
+		g_httpBllPtr->post(hrp);
+	}
+	else
+	{
+        if(specialtyNameList.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取专业信息失败"), this);
+        }
+        else
+        {
+            ShowMsg(specialtyNameList.sMessage, this);
+        }
+	}
+}
+
+void courseList::onCheckExamInProgress(CCheckExamInProgress checkExamInProgress)
+{
+	if (checkExamInProgress.nCode == 200)
+	{
+		if (checkExamInProgress.bHasExamInProgress)
+		{
+			if (checkExamInProgress.bIsExceed)
+			{
+				//超过断点时长交卷
+				
+				
+			}
+			else
+			{
+				//进入断点
+				g_appInfoPtr->m_oExamInfo.nExamId = checkExamInProgress.nExamId;
+				g_appInfoPtr->m_oExamInfo.nExamRecordDataId = checkExamInProgress.nExamRecordDataId;
+				g_appInfoPtr->m_oExamInfo.sExamType = checkExamInProgress.sExamType;
+                g_appInfoPtr->m_oExamInfo.bIsExamInProgress = true;
+
+                if (m_pResumeExam == nullptr)
+                {
+                    m_pResumeExam = std::make_shared<awResumeExam>(this);
+                }
+                m_pResumeExam->show();
+
+                //环境监测
+                CHttpRequestPackage hrp;
+                hrp.sUri = QString("/api/ecs_exam_work/exam/getExamPropertyFromCacheByStudentSession/%1/%2")
+                    .arg(g_appInfoPtr->m_oExamInfo.nExamId).arg("CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE");
+                hrp.nRequestType = RequestType::rtGetExamProperty;
+                hrp.sCommonStr = "CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE";
+                g_httpBllPtr->get(hrp);
+
+			}
+		}
+		else
+		{
+			if (checkExamInProgress.sEnterType == EXAM_INPROGRESS_ENTER_TYPE::EIET_COURSE_LIST)
+			{
+				CHttpRequestPackage hrp;
+				hrp.sUri = "/api/ecs_core/rolePrivilege/getStudentClientMenu";
+				hrp.nRequestType = RequestType::rtGetStudentClientMenu;
+				hrp.sParamList.push_back(QString::fromLocal8Bit("rootOrgId,%1").arg(g_appInfoPtr->m_sRootOrgId));
+				g_httpBllPtr->get(hrp);
+
+				if (m_pWelcomeWidget == nullptr)
+				{
+					m_pWelcomeWidget = std::make_shared<welcomeWidget>(g_appInfoPtr->m_sStudentName, g_appInfoPtr->m_sStudentCode, g_appInfoPtr->m_sSpecialtyName, this);
+					connect(m_pWelcomeWidget.get(), &welcomeWidget::exitWelcomeWidget, this, [&]() {
+						m_pWelcomeWidget.reset();
+					});
+				}
+				m_pWelcomeWidget->show();
+			}
+			else
+			{
+
+                //环境监测
+                CHttpRequestPackage hrp;
+                hrp.sUri = QString("/api/ecs_exam_work/exam/getExamPropertyFromCacheByStudentSession/%1/%2")
+                    .arg(g_appInfoPtr->m_oExamInfo.nExamId).arg("CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE");
+                hrp.nRequestType = RequestType::rtGetExamProperty;
+                hrp.sCommonStr = "CHECK_ENVIRONMENT,IS_FACE_CHECK,SNAPSHOT_INTERVAL,WARN_THRESHOLD,FACE_VERIFY_START_MINUTE,FACE_VERIFY_END_MINUTE,IS_STRANGER_ENABLE";
+                g_httpBllPtr->get(hrp);
+			}						
+		}		
+	}
+	else
+	{
+        if(checkExamInProgress.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取断点信息失败"), this);
+        }
+        else
+        {
+            ShowMsg(checkExamInProgress.sMessage, this);
+        }
+	}
+}
+
+void courseList::onGetStudentClientMenu(CGetStudentClientMenu getStudentClientMenu)
+{
+	if (getStudentClientMenu.nCode == 200)
+	{
+		ui->btn_onlineExam->setVisible(false);
+		ui->btn_onlineHomework->setVisible(false);
+		ui->btn_onlinePractice->setVisible(false);
+		ui->btn_offlineExam->setVisible(false);
+		ui->btn_notice->setVisible(false);
+		ui->btn_editPassword->setVisible(false);
+		for (CStudentClientMenu scm : getStudentClientMenu.vMenus)
+		{
+			if (scm.sCode == "stu_online_exam_0")
+			{
+				ui->btn_onlineExam->setVisible(true);
+			}
+			else if (scm.sCode == "stu_online_homework_0")
+			{
+				ui->btn_onlineHomework->setVisible(true);
+			}
+			else if (scm.sCode == "stu_online_practice_0")
+			{
+				ui->btn_onlinePractice->setVisible(true);
+			}
+			else if (scm.sCode == "stu_offline_exam_0")
+			{
+				ui->btn_offlineExam->setVisible(true);
+			}
+			else if (scm.sCode == "stu_notice_0")
+			{
+				ui->btn_notice->setVisible(true);
+				CHttpRequestPackage hrp;
+				hrp.sUri = "/api/ecs_exam_work/notice/getUserNoticeList";
+				hrp.nRequestType = RequestType::rtGetUserNoticeList;				
+				g_httpBllPtr->get(hrp);
+			}		
+			else if (scm.sCode == "stu_modify_pwd_0")
+			{
+				ui->btn_editPassword->setVisible(true);
+			}
+		}
+		menuBtnRePosistion();
+	}
+	else
+	{
+        if(getStudentClientMenu.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取菜单失败"), this);
+        }
+        else
+        {
+            ShowMsg(getStudentClientMenu.sMessage, this);
+        }
+	}
+}
+
+void courseList::onGetUserNoticeList(CGetUserNoticeList getUserNoticeList)
+{
+	if (getUserNoticeList.nCode == 200)
+	{
+		if (m_pNoticeList == nullptr)
+		{
+			m_pNoticeList = std::make_shared<clNoticeList>(ui->stw_courseList);
+			m_pNoticeList->setUI(ui->stw_courseList->width(), ui->stw_courseList->height());
+		}
+		m_pNoticeList->setNotice(getUserNoticeList.vNoticeList);
+		
+		if (m_pNoticePopWidget == nullptr)
+		{
+			m_pNoticePopWidget = std::make_shared<clNoticePopWidget>(this);
+			m_pNoticePopWidget->setUI(g_appInfoPtr->m_fRate * 960, g_appInfoPtr->m_fRate * 558, g_appInfoPtr->m_fRate * 310, g_appInfoPtr->m_fRate * 200);
+		}
+		
+		for (CNoticeInfo ni : getUserNoticeList.vNoticeList)
+		{
+			if (!ni.bHasRead)
+			{
+				m_pNoticePopWidget->addNotice(ni);
+			}
+		}
+		
+		if(m_pNoticePopWidget->noticeCount() > 0)
+		{
+			ui->btn_noticeNum->setVisible(true);
+			ui->btn_noticeNum->setText(QString::number(m_pNoticePopWidget->noticeCount()));
+			
+			m_pNoticePopWidget->show();
+		}		
+	}
+	else
+	{
+        if(getUserNoticeList.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取通知信息失败"), this);
+        }
+        else
+        {
+            ShowMsg(getUserNoticeList.sMessage, this);
+        }
+	}
+}

+ 115 - 0
client/courseList.h

@@ -0,0 +1,115 @@
+#ifndef COURSELIST_H
+#define COURSELIST_H
+
+#include <QWidget>
+#include <QTimer>
+
+#include "clNoticePopWidget.h"
+#include "clMobileLogin.h"
+#include "clStudentInfo.h"
+#include "clNoticeDetail.h"
+#include "clopResultList.h"
+#include "welcomeWidget.h"
+#include "privacyWidget.h"
+#include "clopReport.h"
+#include "etWhetherEnvTest.h"
+#include "faceCompare.h"
+#include "cloeUploadFile.h"
+#include "awBackground.h"
+#include "clOnlineExam.h"
+#include "clOnlineHomework.h"
+#include "clOnlinePractice.h"
+#include "clOfflineExam.h"
+#include "clNoticeList.h"
+#include "clEditPassword.h"
+#include "CHttpBll.h"
+#include "environmentalTest.h"
+namespace Ui {
+class courseList;
+}
+
+enum class COURSE_MENU_BTN_TYPE
+{
+    cmbt_online_exam = 1,
+    cmbt_online_homework,
+    cmbt_online_practice,
+    cmbt_offline_exam,
+    cmbt_notice,
+    cmbt_edit_password
+};
+
+class courseList : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit courseList(QWidget *parent = nullptr);
+    ~courseList();
+signals:
+    void minisize();
+private slots:
+    void on_btn_minisize_clicked();
+    void on_btn_cl_close_clicked();
+    void on_btn_onlineExam_clicked();
+    void on_btn_onlineHomework_clicked();
+    void on_btn_onlinePractice_clicked();
+    void on_btn_offlineExam_clicked();
+    void on_btn_notice_clicked();
+    void on_btn_editPassword_clicked();
+
+    void on_btn_exit_clicked();
+
+    void hideStudentInfoBtn();
+    void hideMobileLoginBtn();
+    //进入考试
+    void onEnterExam(CL_OPERATION_TYPE cot, __int64 nExamId, __int64 nExamStudentId);
+
+	void onAppDownLoadUrl(CAppDownLoadUrl appDownLoadUrl);
+	void onDownLoadFile(CDownLoadFileInfo downLoadFileInfo);
+	void onGetStudentInfoBySession(CGetStudentInfoBySession getStudentInfoBySession);
+	void onSpecialtyNameList(CSpecialtyNameList specialtyNameList);
+	void onCheckExamInProgress(CCheckExamInProgress checkExamInProgress);
+	void onGetStudentClientMenu(CGetStudentClientMenu getStudentClientMenu);
+	void onGetUserNoticeList(CGetUserNoticeList getUserNoticeList);
+	void onStartExamLimit(CStartExamLimit startExamLimit);
+	void onIpLimit(CIpLimit ipLimit);
+	void onGetExamProperty(CGetExamProperty getExamProperty);
+	void onFaceCheckEnabled(CFaceCheckEnabled faceCheckEnabled);
+	void onLivenessEnabled(CLivenessEnabled livenessEnabled);
+private:
+    void initUI();
+    void setCheck(COURSE_MENU_BTN_TYPE cmbt);
+    bool eventFilter(QObject *obj, QEvent *ev);
+	void menuBtnRePosistion();
+	void getStudentInfoBySession();
+	void logout();
+	void enterWaitExam();
+
+    Ui::courseList *ui;
+
+	std::shared_ptr<QTimer> m_pStudentInfoBtnTimer;
+	std::shared_ptr<QTimer> m_pMobileLoginBtnTimer;
+	std::shared_ptr<QTimer> m_pRefreshTimer;
+    std::shared_ptr<clNoticePopWidget> m_pNoticePopWidget;
+    std::shared_ptr<clMobileLogin> m_pMobileLogin;
+    std::shared_ptr<clStudentInfo> m_pStudentInfo;
+    std::shared_ptr<clNoticeDetail> m_pNoticeDetail;
+    std::shared_ptr<clopResultList> m_pClopResultList;
+    std::shared_ptr<welcomeWidget> m_pWelcomeWidget;
+    std::shared_ptr<privacyWidget> m_pPrivacyWidget;
+	std::shared_ptr<clOnlineExam> m_pOnlineExam;
+	std::shared_ptr<clOnlineHomework> m_pOnlineHomework;
+	std::shared_ptr<clOnlinePractice> m_pOnlinePractice;
+	std::shared_ptr<clOfflineExam> m_pOfflineExam;
+	std::shared_ptr<clNoticeList> m_pNoticeList;
+	std::shared_ptr<clEditPassword> m_pEditPassword;
+    std::shared_ptr<clopReport> m_pClopReport;
+    std::shared_ptr<etWhetherEnvTest> m_pWhetherEnvTest;
+    std::shared_ptr<environmentalTest> m_pEnvironmentalTest;
+    std::shared_ptr<faceCompare> m_pFaceCompare;
+    std::shared_ptr<cloeUploadFile> m_pCloeUploadFile;
+    std::shared_ptr<awBackground> m_pBackground;
+    std::shared_ptr<awResumeExam> m_pResumeExam;
+};
+
+#endif // COURSELIST_H

+ 338 - 0
client/courseList.ui

@@ -0,0 +1,338 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>courseList</class>
+ <widget class="QWidget" name="courseList">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>730</width>
+    <height>605</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_cl_BG" native="true">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>731</width>
+     <height>611</height>
+    </rect>
+   </property>
+   <property name="styleSheet">
+    <string notr="true"/>
+   </property>
+   <widget class="QWidget" name="widget_top" native="true">
+    <property name="geometry">
+     <rect>
+      <x>120</x>
+      <y>0</y>
+      <width>611</width>
+      <height>51</height>
+     </rect>
+    </property>
+    <widget class="QPushButton" name="btn_minisize">
+     <property name="geometry">
+      <rect>
+       <x>530</x>
+       <y>10</y>
+       <width>31</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_VLine">
+     <property name="geometry">
+      <rect>
+       <x>500</x>
+       <y>10</y>
+       <width>16</width>
+       <height>21</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_mobileLogin">
+     <property name="geometry">
+      <rect>
+       <x>234</x>
+       <y>10</y>
+       <width>131</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>手机端登录(Android)</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_studentInfo">
+     <property name="geometry">
+      <rect>
+       <x>380</x>
+       <y>10</y>
+       <width>75</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>何乐乐</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_currentPlaceIcon">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>20</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_currrentPlace">
+     <property name="geometry">
+      <rect>
+       <x>80</x>
+       <y>20</y>
+       <width>141</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>当前所在位置:在线考试</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_cl_close">
+     <property name="geometry">
+      <rect>
+       <x>570</x>
+       <y>10</y>
+       <width>31</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_exit">
+     <property name="geometry">
+      <rect>
+       <x>470</x>
+       <y>10</y>
+       <width>21</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_arrow">
+     <property name="geometry">
+      <rect>
+       <x>290</x>
+       <y>30</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QWidget" name="widget_menu" native="true">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>0</y>
+      <width>120</width>
+      <height>611</height>
+     </rect>
+    </property>
+    <widget class="QPushButton" name="btn_onlineExam">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>120</y>
+       <width>75</width>
+       <height>61</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>在线
+考试</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_cl_org_icon">
+     <property name="geometry">
+      <rect>
+       <x>30</x>
+       <y>40</y>
+       <width>54</width>
+       <height>51</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_onlineHomework">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>190</y>
+       <width>75</width>
+       <height>61</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>在线
+作业</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_onlinePractice">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>260</y>
+       <width>75</width>
+       <height>61</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>在线
+练习</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_offlineExam">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>330</y>
+       <width>75</width>
+       <height>61</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>离线
+考试</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_notice">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>400</y>
+       <width>75</width>
+       <height>61</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>公告
+通知</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_editPassword">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>470</y>
+       <width>75</width>
+       <height>61</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>修改
+密码</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_noticeNum">
+     <property name="geometry">
+      <rect>
+       <x>80</x>
+       <y>400</y>
+       <width>16</width>
+       <height>21</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>1</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QWidget" name="widget_bottom" native="true">
+    <property name="geometry">
+     <rect>
+      <x>120</x>
+      <y>549</y>
+      <width>611</width>
+      <height>51</height>
+     </rect>
+    </property>
+    <widget class="QLabel" name="label_cl_company">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>30</y>
+       <width>241</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>技术支持:武汉启明泰和软件服务有限公司</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_cl_version">
+     <property name="geometry">
+      <rect>
+       <x>510</x>
+       <y>30</y>
+       <width>81</width>
+       <height>16</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>版本号:V1.0</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_serverTime">
+     <property name="geometry">
+      <rect>
+       <x>310</x>
+       <y>30</y>
+       <width>191</width>
+       <height>20</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>服务器时间:2021-08-10 14:42:27</string>
+     </property>
+    </widget>
+   </widget>
+   <widget class="QStackedWidget" name="stw_courseList">
+    <property name="geometry">
+     <rect>
+      <x>120</x>
+      <y>50</y>
+      <width>611</width>
+      <height>501</height>
+     </rect>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 316 - 0
client/environmentalTest.cpp

@@ -0,0 +1,316 @@
+#include "environmentalTest.h"
+#include "ui_environmentalTest.h"
+
+#include <QDesktopWidget>
+
+#include "CAppInfo.h"
+
+
+environmentalTest::environmentalTest(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::environmentalTest)
+{
+    ui->setupUi(this);
+
+    setWindowFlags(Qt::FramelessWindowHint);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+    m_curentStep = ENV_TEST_STEP::ett_network;
+    initUI();
+}
+
+environmentalTest::~environmentalTest()
+{
+    delete ui;
+}
+
+void environmentalTest::initUI()
+{
+    QDesktopWidget *dekwiget = QApplication::desktop();
+    setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+    ui->widget_mask->setGeometry(0, 0, dekwiget->width(), dekwiget->height());
+    ui->widget_et_BG->setGeometry((width() - g_appInfoPtr->m_fRate*800)/2, (height() - g_appInfoPtr->m_fRate*377)/2,
+                                   g_appInfoPtr->m_fRate*800, g_appInfoPtr->m_fRate*377);
+    ui->label_et_title->adjustSize();
+    ui->label_et_title->setGeometry(g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*16,
+                                    ui->label_et_title->width(), ui->label_et_title->height());
+    ui->widget_et_steps->setGeometry(0, ui->label_et_title->y() + ui->label_et_title->height() + g_appInfoPtr->m_fRate*16,
+                                     ui->widget_et_BG->width(), g_appInfoPtr->m_fRate*86);
+    ui->label_et_networkIcon->setGeometry(g_appInfoPtr->m_fRate*55, g_appInfoPtr->m_fRate*20, g_appInfoPtr->m_fRate*24, g_appInfoPtr->m_fRate*24);
+    ui->label_et_network->adjustSize();
+    ui->label_et_network->setGeometry(ui->label_et_networkIcon->x() + (ui->label_et_networkIcon->width() - ui->label_et_network->width())/2,
+                                      ui->label_et_networkIcon->y() + ui->label_et_networkIcon->height() + g_appInfoPtr->m_fRate*6,
+                                      ui->label_et_network->width(), ui->label_et_network->height());
+    ui->label_et_HLine1->setGeometry(ui->label_et_networkIcon->x() + ui->label_et_networkIcon->width()/2, ui->label_et_networkIcon->y() + (ui->label_et_networkIcon->height() - g_appInfoPtr->m_fRate*1)/2,
+                                     g_appInfoPtr->m_fRate*109 + ui->label_et_networkIcon->width(), g_appInfoPtr->m_fRate*1);
+    ui->label_et_timeIcon->setGeometry(ui->label_et_networkIcon->x() + ui->label_et_HLine1->width(), ui->label_et_networkIcon->y(),
+                                       ui->label_et_networkIcon->width(), ui->label_et_networkIcon->height());
+    ui->label_et_time->adjustSize();
+    ui->label_et_time->setGeometry(ui->label_et_timeIcon->x() + (ui->label_et_timeIcon->width() - ui->label_et_time->width())/2,
+                                   ui->label_et_network->y(), ui->label_et_time->width(), ui->label_et_time->height());
+    ui->label_et_HLine2->setGeometry(ui->label_et_HLine1->x() + ui->label_et_HLine1->width(), ui->label_et_HLine1->y(),
+                                     ui->label_et_HLine1->width(), ui->label_et_HLine1->height());
+    ui->label_et_cameraIcon->setGeometry(ui->label_et_timeIcon->x() + ui->label_et_HLine1->width(), ui->label_et_networkIcon->y(),
+                                       ui->label_et_networkIcon->width(), ui->label_et_networkIcon->height());
+    ui->label_et_camera->adjustSize();
+    ui->label_et_camera->setGeometry(ui->label_et_cameraIcon->x() + (ui->label_et_cameraIcon->width() - ui->label_et_camera->width())/2,
+                                   ui->label_et_network->y(), ui->label_et_camera->width(), ui->label_et_camera->height());
+    ui->label_et_HLine3->setGeometry(ui->label_et_HLine2->x() + ui->label_et_HLine1->width(), ui->label_et_HLine1->y(),
+                                     ui->label_et_HLine1->width(), ui->label_et_HLine1->height());
+    ui->label_et_voiceIcon->setGeometry(ui->label_et_cameraIcon->x() + ui->label_et_HLine1->width(), ui->label_et_networkIcon->y(),
+                                       ui->label_et_networkIcon->width(), ui->label_et_networkIcon->height());
+    ui->label_et_voice->adjustSize();
+    ui->label_et_voice->setGeometry(ui->label_et_voiceIcon->x() + (ui->label_et_voiceIcon->width() - ui->label_et_voice->width())/2,
+                                   ui->label_et_network->y(), ui->label_et_voice->width(), ui->label_et_voice->height());
+    ui->label_et_HLine4->setGeometry(ui->label_et_HLine3->x() + ui->label_et_HLine1->width(), ui->label_et_HLine1->y(),
+                                     ui->label_et_HLine1->width(), ui->label_et_HLine1->height());
+    ui->label_et_mobileIcon->setGeometry(ui->label_et_voiceIcon->x() + ui->label_et_HLine1->width(), ui->label_et_networkIcon->y(),
+                                       ui->label_et_networkIcon->width(), ui->label_et_networkIcon->height());
+    ui->label_et_mobile->adjustSize();
+    ui->label_et_mobile->setGeometry(ui->label_et_mobileIcon->x() + (ui->label_et_mobileIcon->width() - ui->label_et_mobile->width())/2,
+                                   ui->label_et_network->y(), ui->label_et_mobile->width(), ui->label_et_mobile->height());
+    ui->label_et_HLine5->setGeometry(ui->label_et_HLine4->x() + ui->label_et_HLine1->width(), ui->label_et_HLine1->y(),
+                                     ui->label_et_HLine1->width(), ui->label_et_HLine1->height());
+    ui->label_et_resultIcon->setGeometry(ui->label_et_mobileIcon->x() + ui->label_et_HLine1->width(), ui->label_et_networkIcon->y(),
+                                       ui->label_et_networkIcon->width(), ui->label_et_networkIcon->height());
+    ui->label_et_result->adjustSize();
+    ui->label_et_result->setGeometry(ui->label_et_resultIcon->x() + (ui->label_et_resultIcon->width() - ui->label_et_result->width())/2,
+                                   ui->label_et_network->y(), ui->label_et_result->width(), ui->label_et_result->height());
+    ui->btn_nextStep->setGeometry(ui->widget_et_BG->width() - g_appInfoPtr->m_fRate*(30 + 80), ui->widget_et_BG->height() - g_appInfoPtr->m_fRate*(30 + 30),
+                                  g_appInfoPtr->m_fRate*80, g_appInfoPtr->m_fRate*30);
+    ui->btn_previousStep->setGeometry(ui->btn_nextStep->x() - g_appInfoPtr->m_fRate*10 - ui->btn_nextStep->width(), ui->btn_nextStep->y(),
+                                      ui->btn_nextStep->width(), ui->btn_nextStep->height());
+    changeStepUI(m_curentStep);
+}
+
+void environmentalTest::changeStepUI(ENV_TEST_STEP step)
+{
+    QString sHLineNormalStyle = QString(R"(QLabel
+                                        {
+                                            background:rgba(218,217,229,1);
+                                        })");
+    QString sHLineHoverStyle = QString(R"(QLabel
+                                        {
+                                            background:rgba(19,187,138,1);
+                                        })");
+    QString sNumNormalStyle = QString(R"(QLabel
+                                      {
+                                          border:1px solid rgba(105,105,105,1);
+                                          border-radius:%1px;
+                                          background:rgba(244,243,247,1);
+                                          font-size:%2px;
+                                          font-family:"Microsoft YaHei";
+                                          font-weight:600;
+                                          color:rgba(102, 102, 102,1);
+                                      })").arg((int)(g_appInfoPtr->m_fRate*12)).arg((int)(g_appInfoPtr->m_fRate*12));
+    QString sNumHoverStyle = QString(R"(QLabel
+                                      {
+                                          border:0px solid rgba(105,105,105,1);
+                                          border-radius:%1px;
+                                          background:rgba(19,187,138,1);
+                                          font-size:%2px;
+                                          font-family:"Microsoft YaHei";
+                                          font-weight:600;
+                                          color:rgba(255, 255, 255,1);
+                                      })").arg((int)(g_appInfoPtr->m_fRate*12)).arg((int)(g_appInfoPtr->m_fRate*12));
+	QString sTextNormalStyle = QString(R"(QLabel
+                                      { 
+                                          font-size:%1px;
+                                          font-family:"Microsoft YaHei";
+                                          font-weight:600;
+                                          color:rgba(102, 102, 102,1);
+                                      })").arg((int)(g_appInfoPtr->m_fRate * 12));
+	QString sTextHoverStyle = QString(R"(QLabel
+                                      {
+                                          font-size:%1px;
+                                          font-family:"Microsoft YaHei";
+                                          font-weight:600;
+                                          color:rgba(19, 187, 138,1);
+                                      })").arg((int)(g_appInfoPtr->m_fRate * 12));
+    int nWidgetHeight = 0;
+    int nHeight = 0;
+	ui->btn_nextStep->setText(QString::fromLocal8Bit("下一步"));
+    if(step == ENV_TEST_STEP::ett_network)
+    {
+		if(m_pTimeTest !=nullptr)
+		{ 
+			m_pTimeTest->hide();
+		}
+		
+        if(m_pNetworkTest == nullptr)
+        {
+            m_pNetworkTest = std::make_shared<etNetworkTest>(ui->widget_et_BG);
+			nHeight = m_pNetworkTest->setUI(0, ui->widget_et_steps->y() + ui->widget_et_steps->height(), ui->widget_et_BG->width());
+        }
+		nHeight = m_pNetworkTest->widgetHeight();
+		m_pNetworkTest->show();
+		ui->label_et_HLine1->setStyleSheet(sHLineNormalStyle);
+		ui->label_et_network->setStyleSheet(sTextHoverStyle);
+		ui->label_et_networkIcon->setStyleSheet(sNumHoverStyle);
+		ui->label_et_time->setStyleSheet(sTextNormalStyle);
+		ui->label_et_timeIcon->setStyleSheet(sNumNormalStyle);
+    }
+    else if(step == ENV_TEST_STEP::ett_time)
+    {
+		if (m_pNetworkTest != nullptr)
+		{
+			m_pNetworkTest->hide();
+		}
+		if (m_pCameraTest != nullptr)
+		{
+			m_pCameraTest->hide();
+		}
+        if(m_pTimeTest == nullptr)
+        {
+            m_pTimeTest = std::make_shared<etTimeTest>(ui->widget_et_BG);
+			nHeight = m_pTimeTest->setUI(0, ui->widget_et_steps->y() + ui->widget_et_steps->height(), ui->widget_et_BG->width());
+        }
+		nHeight = m_pTimeTest->widgetHeight();
+		m_pTimeTest->show();
+		ui->label_et_HLine1->setStyleSheet(sHLineHoverStyle);
+        ui->label_et_HLine2->setStyleSheet(sHLineNormalStyle);
+		ui->label_et_network->setStyleSheet(sTextNormalStyle);
+		ui->label_et_networkIcon->setStyleSheet(sNumNormalStyle);
+		ui->label_et_camera->setStyleSheet(sTextNormalStyle);
+		ui->label_et_cameraIcon->setStyleSheet(sNumNormalStyle);
+		ui->label_et_time->setStyleSheet(sTextHoverStyle);
+		ui->label_et_timeIcon->setStyleSheet(sNumHoverStyle);
+    }
+    else if(step == ENV_TEST_STEP::ett_camera)
+    {
+		if (m_pTimeTest != nullptr)
+		{
+			m_pTimeTest->hide();
+		}
+		if (m_pVoiceTest != nullptr)
+		{
+			m_pVoiceTest->hide();
+		}
+        if(m_pCameraTest == nullptr)
+        {
+            m_pCameraTest = std::make_shared<etCameraTest>(ui->widget_et_BG);
+			nHeight = m_pCameraTest->setUI(0, ui->widget_et_steps->y() + ui->widget_et_steps->height(), ui->widget_et_BG->width());
+        }
+		nHeight = m_pCameraTest->widgetHeight();
+		m_pCameraTest->show();
+		ui->label_et_HLine2->setStyleSheet(sHLineHoverStyle);
+        ui->label_et_HLine3->setStyleSheet(sHLineNormalStyle);
+		ui->label_et_camera->setStyleSheet(sTextHoverStyle);
+		ui->label_et_cameraIcon->setStyleSheet(sNumHoverStyle);
+		ui->label_et_time->setStyleSheet(sTextNormalStyle);
+		ui->label_et_timeIcon->setStyleSheet(sNumNormalStyle);
+		ui->label_et_voice->setStyleSheet(sTextNormalStyle);
+		ui->label_et_voiceIcon->setStyleSheet(sNumNormalStyle);
+    }
+    else if(step == ENV_TEST_STEP::ett_voice)
+    {
+		if (m_pCameraTest != nullptr)
+		{
+			m_pCameraTest->hide();
+		}
+		if (m_pMobileTest != nullptr)
+		{
+			m_pMobileTest->hide();
+		}
+
+        if(m_pVoiceTest == nullptr)
+        {
+            m_pVoiceTest = std::make_shared<etVoiceTest>(ui->widget_et_BG);
+			nHeight = m_pVoiceTest->setUI(0, ui->widget_et_steps->y() + ui->widget_et_steps->height(), ui->widget_et_BG->width());
+        }
+		nHeight = m_pVoiceTest->widgetHeight();
+		m_pVoiceTest->show();
+		ui->label_et_HLine3->setStyleSheet(sHLineHoverStyle);
+        ui->label_et_HLine4->setStyleSheet(sHLineNormalStyle);
+		ui->label_et_camera->setStyleSheet(sTextNormalStyle);
+		ui->label_et_cameraIcon->setStyleSheet(sNumNormalStyle);
+		ui->label_et_voice->setStyleSheet(sTextHoverStyle);
+		ui->label_et_voiceIcon->setStyleSheet(sNumHoverStyle);
+		ui->label_et_mobile->setStyleSheet(sTextNormalStyle);
+		ui->label_et_mobileIcon->setStyleSheet(sNumNormalStyle);
+    }
+    else if(step == ENV_TEST_STEP::ett_mobile)
+    {
+		if (m_pVoiceTest != nullptr)
+		{
+			m_pVoiceTest->hide();
+		}
+		if (m_pTestResult != nullptr)
+		{
+			m_pTestResult->hide();
+		}
+        if(m_pMobileTest == nullptr)
+        {
+            m_pMobileTest = std::make_shared<etMobileTest>(ui->widget_et_BG);
+			nHeight = m_pMobileTest->setUI(0, ui->widget_et_steps->y() + ui->widget_et_steps->height(), ui->widget_et_BG->width());
+        }
+		nHeight = m_pMobileTest->widgetHeight();
+		m_pMobileTest->show();
+		ui->label_et_HLine4->setStyleSheet(sHLineHoverStyle);
+        ui->label_et_HLine5->setStyleSheet(sHLineNormalStyle);
+		ui->label_et_voice->setStyleSheet(sTextNormalStyle);
+		ui->label_et_voiceIcon->setStyleSheet(sNumNormalStyle);
+		ui->label_et_mobile->setStyleSheet(sTextHoverStyle);
+		ui->label_et_mobileIcon->setStyleSheet(sNumHoverStyle);
+		ui->label_et_result->setStyleSheet(sTextNormalStyle);
+		ui->label_et_resultIcon->setStyleSheet(sNumNormalStyle);
+    }
+    else if(step == ENV_TEST_STEP::ett_result)
+    {
+		if (m_pMobileTest != nullptr)
+		{
+			m_pMobileTest->hide();
+		}
+        if(m_pTestResult == nullptr)
+        {
+            m_pTestResult = std::make_shared<etTestResult>(ui->widget_et_BG);
+			//nHeight = m_pTestResult->setUI(0, ui->widget_et_steps->y() + ui->widget_et_steps->height(), ui->widget_et_BG->width());
+        }
+		//nHeight = m_pTestResult->widgetHeight();
+		int nNetworkStatus = m_pNetworkTest->getCheckStatus();
+		int nTimekStatus = m_pTimeTest->getCheckStatus();
+		int nCameraStatus = m_pCameraTest->getCheckStatus();
+		int nVoiceStatus = m_pVoiceTest->getCheckStatus();
+		int nWXStatus = m_pMobileTest->getCheckStatus();
+		m_pTestResult->setResult(nNetworkStatus, nTimekStatus, nCameraStatus,
+			nVoiceStatus, nWXStatus);
+		nHeight = m_pTestResult->setUI(0, ui->widget_et_steps->y() + ui->widget_et_steps->height(), ui->widget_et_BG->width());
+		m_pTestResult->show();
+        ui->label_et_HLine5->setStyleSheet(sHLineHoverStyle);
+		ui->label_et_mobile->setStyleSheet(sTextNormalStyle);
+		ui->label_et_mobileIcon->setStyleSheet(sNumNormalStyle);
+		ui->label_et_result->setStyleSheet(sTextHoverStyle);
+		ui->label_et_resultIcon->setStyleSheet(sNumHoverStyle);
+		ui->btn_nextStep->setText(QString::fromLocal8Bit("进入考试"));
+    }
+    nWidgetHeight = ui->widget_et_steps->y() + ui->widget_et_steps->height() + nHeight + g_appInfoPtr->m_fRate*90;
+    ui->widget_et_BG->setGeometry((width() - g_appInfoPtr->m_fRate*800)/2, (height() - nWidgetHeight)/2,
+                                   g_appInfoPtr->m_fRate*800, nWidgetHeight);
+    ui->btn_nextStep->setGeometry(ui->widget_et_BG->width() - g_appInfoPtr->m_fRate*(30 + 80), ui->widget_et_BG->height() - g_appInfoPtr->m_fRate*(30 + 30),
+                                  g_appInfoPtr->m_fRate*80, g_appInfoPtr->m_fRate*30);
+    ui->btn_previousStep->setGeometry(ui->btn_nextStep->x() - g_appInfoPtr->m_fRate*10 - ui->btn_nextStep->width(), ui->btn_nextStep->y(),
+                                      ui->btn_nextStep->width(), ui->btn_nextStep->height());
+}
+
+void environmentalTest::on_btn_previousStep_clicked()
+{
+    if(m_curentStep > ENV_TEST_STEP::ett_network)
+    {
+        m_curentStep = static_cast<ENV_TEST_STEP>(static_cast<int>(m_curentStep) - 1);
+        changeStepUI(m_curentStep);
+    }
+}
+
+void environmentalTest::on_btn_nextStep_clicked()
+{
+    if(m_curentStep < ENV_TEST_STEP::ett_result)
+    {
+        m_curentStep = static_cast<ENV_TEST_STEP>(static_cast<int>(m_curentStep) + 1);
+        changeStepUI(m_curentStep);
+    }
+    else
+    {
+        emit enterExam();
+    }
+}

+ 57 - 0
client/environmentalTest.h

@@ -0,0 +1,57 @@
+#ifndef ENVIRONMENTALTEST_H
+#define ENVIRONMENTALTEST_H
+
+#include <QWidget>
+
+
+#include "etCameraTest.h"
+#include "etVoiceTest.h"
+#include "etMobileTest.h"
+#include "etTestResult.h"
+#include "etNetworkTest.h"
+#include "etTimeTest.h"
+
+namespace Ui {
+class environmentalTest;
+}
+
+enum class ENV_TEST_STEP
+{
+    ett_network = 1,
+    ett_time,
+    ett_camera,
+    ett_voice,
+    ett_mobile,
+    ett_result
+};
+
+class environmentalTest : public QWidget
+{
+    Q_OBJECT
+signals:
+    void enterExam();
+public:
+    explicit environmentalTest(QWidget *parent = nullptr);
+    ~environmentalTest();
+
+private slots:
+    void on_btn_previousStep_clicked();
+
+    void on_btn_nextStep_clicked();
+
+private:
+    void initUI();
+    void changeStepUI(ENV_TEST_STEP step);
+
+    Ui::environmentalTest *ui;
+    std::shared_ptr<etNetworkTest> m_pNetworkTest;
+    std::shared_ptr<etTimeTest> m_pTimeTest;
+    std::shared_ptr<etCameraTest> m_pCameraTest;
+    std::shared_ptr<etVoiceTest> m_pVoiceTest;
+    std::shared_ptr<etMobileTest> m_pMobileTest;
+    std::shared_ptr<etTestResult> m_pTestResult;
+
+    ENV_TEST_STEP m_curentStep;
+};
+
+#endif // ENVIRONMENTALTEST_H

+ 345 - 0
client/environmentalTest.ui

@@ -0,0 +1,345 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>environmentalTest</class>
+ <widget class="QWidget" name="environmentalTest">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>820</width>
+    <height>618</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_mask" native="true">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>10</y>
+     <width>831</width>
+     <height>681</height>
+    </rect>
+   </property>
+   <widget class="QWidget" name="widget_et_BG" native="true">
+    <property name="geometry">
+     <rect>
+      <x>10</x>
+      <y>10</y>
+      <width>671</width>
+      <height>501</height>
+     </rect>
+    </property>
+    <widget class="QPushButton" name="btn_nextStep">
+     <property name="geometry">
+      <rect>
+       <x>420</x>
+       <y>360</y>
+       <width>75</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>下一步</string>
+     </property>
+    </widget>
+    <widget class="QPushButton" name="btn_previousStep">
+     <property name="geometry">
+      <rect>
+       <x>330</x>
+       <y>360</y>
+       <width>75</width>
+       <height>23</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>上一步</string>
+     </property>
+    </widget>
+    <widget class="QLabel" name="label_et_title">
+     <property name="geometry">
+      <rect>
+       <x>20</x>
+       <y>20</y>
+       <width>54</width>
+       <height>12</height>
+      </rect>
+     </property>
+     <property name="text">
+      <string>环境检测</string>
+     </property>
+    </widget>
+    <widget class="QWidget" name="widget_et_steps" native="true">
+     <property name="geometry">
+      <rect>
+       <x>0</x>
+       <y>60</y>
+       <width>831</width>
+       <height>80</height>
+      </rect>
+     </property>
+     <widget class="QLabel" name="label_et_HLine1">
+      <property name="geometry">
+       <rect>
+        <x>70</x>
+        <y>40</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string/>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_HLine2">
+      <property name="geometry">
+       <rect>
+        <x>180</x>
+        <y>40</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string/>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_HLine3">
+      <property name="geometry">
+       <rect>
+        <x>280</x>
+        <y>40</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string/>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_HLine4">
+      <property name="geometry">
+       <rect>
+        <x>380</x>
+        <y>40</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string/>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_HLine5">
+      <property name="geometry">
+       <rect>
+        <x>480</x>
+        <y>40</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string/>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_networkIcon">
+      <property name="geometry">
+       <rect>
+        <x>20</x>
+        <y>30</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>1</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_network">
+      <property name="geometry">
+       <rect>
+        <x>20</x>
+        <y>50</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>网速</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_timeIcon">
+      <property name="geometry">
+       <rect>
+        <x>130</x>
+        <y>30</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>2</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_time">
+      <property name="geometry">
+       <rect>
+        <x>130</x>
+        <y>50</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>时钟</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_cameraIcon">
+      <property name="geometry">
+       <rect>
+        <x>230</x>
+        <y>30</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>3</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_camera">
+      <property name="geometry">
+       <rect>
+        <x>230</x>
+        <y>50</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>摄像头</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_voiceIcon">
+      <property name="geometry">
+       <rect>
+        <x>330</x>
+        <y>30</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>4</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_voice">
+      <property name="geometry">
+       <rect>
+        <x>330</x>
+        <y>50</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>声音</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_mobileIcon">
+      <property name="geometry">
+       <rect>
+        <x>430</x>
+        <y>30</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>5</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_mobile">
+      <property name="geometry">
+       <rect>
+        <x>430</x>
+        <y>60</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>微信小程序</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_resultIcon">
+      <property name="geometry">
+       <rect>
+        <x>520</x>
+        <y>30</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>6</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+     <widget class="QLabel" name="label_et_result">
+      <property name="geometry">
+       <rect>
+        <x>520</x>
+        <y>60</y>
+        <width>54</width>
+        <height>12</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>检测结果</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+    </widget>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 207 - 0
client/etCameraTest.cpp

@@ -0,0 +1,207 @@
+#include "etCameraTest.h"
+#include "ui_etCameraTest.h"
+
+#include <QScrollBar>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPalette>
+
+#include "CAppInfo.h"
+#include "awMsgBox.h"
+#include "logproc.h"
+#include "CCommonTools.h"
+
+etCameraTest::etCameraTest(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::etCameraTest)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	m_nCameraOpenStatus = 2;
+
+    m_pinitTimer = std::make_shared<QTimer>();
+    m_pinitTimer->setInterval(1000);
+    connect(m_pinitTimer.get(), &QTimer::timeout, this, [&](){
+        m_pinitTimer->stop();
+		bool bOpened = true;
+        if(!m_cam.open(0))
+        {
+            ShowMsg(QString::fromLocal8Bit("打开摄像头失败"), this);
+			bOpened = false;
+        }
+		
+        int inWidth = ui->widget_etct_camera->width();
+        m_cam.set(CV_CAP_PROP_FRAME_WIDTH, inWidth);
+        int inHeight = ui->widget_etct_camera->height();
+        m_cam.set(CV_CAP_PROP_FRAME_HEIGHT, inHeight);
+        m_cam.set(CV_CAP_PROP_FPS, 5);
+        m_pVideoTimer->start();
+
+		ui->tablewt_envTest->item(0, 0)->setText(QString::fromLocal8Bit("摄像头正常启用"));
+		QWidget *widget = new QWidget;
+		QHBoxLayout *layout = new QHBoxLayout;
+		widget->setLayout(layout);
+		QLabel *icon = new QLabel;
+
+		if(bOpened)
+		{ 
+			ui->tablewt_envTest->item(0, 1)->setText(QString::fromLocal8Bit("正常"));
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+			icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+		}
+		else
+		{
+			ui->tablewt_envTest->item(0, 1)->setText(QString::fromLocal8Bit("异常"));
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+            icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+		}
+				
+		layout->addWidget(icon);
+		layout->addStretch();
+		ui->tablewt_envTest->setCellWidget(0, 2, widget);
+    });
+
+    m_pVideoTimer = std::make_shared<QTimer>();
+    m_pVideoTimer->setInterval(200);
+    connect(m_pVideoTimer.get(), &QTimer::timeout, this, [&](){
+
+        cv::Mat frame;
+        m_cam>>frame;
+
+        if(frame.empty())
+        {
+            qDebug()<<"widgetCameraTest frame is empty";
+            return;
+        }
+
+        QImage img = CCommonTools::Mat2QImage(frame);
+        ui->widget_etct_camera->setAutoFillBackground(true);
+        QPalette palette;
+        palette.setBrush(QPalette::Window, QBrush(img.scaled(ui->widget_etct_camera->width(), ui->widget_etct_camera->height())));
+        ui->widget_etct_camera->setPalette(palette);
+    });
+
+	
+}
+
+etCameraTest::~etCameraTest()
+{
+    m_pVideoTimer->stop();
+	m_cam.release();
+    delete ui;
+}
+
+int etCameraTest::setUI(const int nLeft, const int nTop, const int nWidth)
+{
+    setGeometry(nLeft, nTop, nWidth, g_appInfoPtr->m_fRate*507);
+    ui->widget_etct_camera->setGeometry(g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*30,
+                                        g_appInfoPtr->m_fRate*536, g_appInfoPtr->m_fRate*300);
+    ui->btn_IsNotSelf->setGeometry(ui->widget_etct_camera->x() + ui->widget_etct_camera->width() + g_appInfoPtr->m_fRate*20,
+                                   ui->widget_etct_camera->y(), g_appInfoPtr->m_fRate*184, g_appInfoPtr->m_fRate*140);
+    ui->btn_IsNotSelf->setIconSize(QSize(g_appInfoPtr->m_fRate*32, g_appInfoPtr->m_fRate*32));
+    ui->btn_IsNotSelf->setIcon(QIcon(":/images/icon-is-not-self.png"));
+    ui->btn_IsSelf->setGeometry(ui->btn_IsNotSelf->x(), ui->btn_IsNotSelf->y() + ui->btn_IsNotSelf->height() + g_appInfoPtr->m_fRate*20,
+                                ui->btn_IsNotSelf->width(), ui->btn_IsNotSelf->height());
+    ui->btn_IsSelf->setIconSize(QSize(g_appInfoPtr->m_fRate*32, g_appInfoPtr->m_fRate*32));
+    ui->btn_IsSelf->setIcon(QIcon(":/images/icon-is-self.png"));
+    ui->label_HLine->setGeometry(ui->widget_etct_camera->x(), ui->widget_etct_camera->y() + ui->widget_etct_camera->height() + g_appInfoPtr->m_fRate*30,
+                                 width() - g_appInfoPtr->m_fRate*30*2, g_appInfoPtr->m_fRate*1);
+
+    ui->tablewt_envTest->setGeometry(ui->widget_etct_camera->x(), ui->label_HLine->y() + ui->label_HLine->height(),
+                                     width() - g_appInfoPtr->m_fRate*30*2, height() - ui->label_HLine->y() - ui->label_HLine->height());
+
+    ui->tablewt_envTest->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablewt_envTest->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablewt_envTest->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablewt_envTest->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablewt_envTest->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablewt_envTest->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablewt_envTest->setRowCount(2);
+    //设置列数
+    ui->tablewt_envTest->setColumnCount(3);
+
+    ui->tablewt_envTest->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablewt_envTest->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+    QStringList sHeadStr;
+    sHeadStr << QString::fromLocal8Bit("检测项") <<
+                QString::fromLocal8Bit("值") <<
+                QString::fromLocal8Bit("状态");
+
+    ui->tablewt_envTest->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablewt_envTest->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablewt_envTest->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablewt_envTest->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    ui->tablewt_envTest->setColumnWidth(0, g_appInfoPtr->m_fRate*525);
+    ui->tablewt_envTest->setColumnWidth(1, g_appInfoPtr->m_fRate*170);
+    ui->tablewt_envTest->setColumnWidth(2, g_appInfoPtr->m_fRate*24);
+    ui->tablewt_envTest->horizontalHeader()->setStretchLastSection(true);
+
+    for(int i = 0; i < 2; i++)
+    {
+        ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("")));
+        ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("")));
+   /*     QWidget *widget = new QWidget;
+        QHBoxLayout *layout = new QHBoxLayout;
+        widget->setLayout(layout);
+        QLabel *icon = new QLabel;
+        icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+        icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+        layout->addWidget(icon);
+        layout->addStretch();
+        ui->tablewt_envTest->setCellWidget(i, 2, widget);*/
+    }
+	m_pinitTimer->start();
+    return height();
+}
+
+int etCameraTest::widgetHeight()
+{
+    return height();
+}
+
+int etCameraTest::getCheckStatus()
+{
+    return m_nCameraOpenStatus;
+}
+
+void etCameraTest::on_btn_IsSelf_clicked()
+{
+    ui->tablewt_envTest->item(1, 0)->setText(QString::fromLocal8Bit("视频显示的是电脑操作者本人"));
+    ui->tablewt_envTest->item(1, 1)->setText(QString::fromLocal8Bit("正常"));
+    QWidget *widget = new QWidget;
+    QHBoxLayout *layout = new QHBoxLayout;
+    widget->setLayout(layout);
+    QLabel *icon = new QLabel;
+    icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+    icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+    layout->addWidget(icon);
+    layout->addStretch();
+    ui->tablewt_envTest->setCellWidget(1, 2, widget);
+	m_nCameraOpenStatus = 1;
+}
+
+void etCameraTest::on_btn_IsNotSelf_clicked()
+{
+    ui->tablewt_envTest->item(1, 0)->setText(QString::fromLocal8Bit("视频显示的是电脑操作者本人"));
+    ui->tablewt_envTest->item(1, 1)->setText(QString::fromLocal8Bit("请检查摄像头"));
+    QWidget *widget = new QWidget;
+    QHBoxLayout *layout = new QHBoxLayout;
+    widget->setLayout(layout);
+    QLabel *icon = new QLabel;
+    icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+    icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+    layout->addWidget(icon);
+    layout->addStretch();
+    ui->tablewt_envTest->setCellWidget(1, 2, widget);
+	m_nCameraOpenStatus = 0;
+}

+ 36 - 0
client/etCameraTest.h

@@ -0,0 +1,36 @@
+#ifndef ETCAMERATEST_H
+#define ETCAMERATEST_H
+
+#include <QWidget>
+#include <opencv2/opencv.hpp>
+
+#include <QTimer>
+
+namespace Ui {
+class etCameraTest;
+}
+
+class etCameraTest : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit etCameraTest(QWidget *parent = nullptr);
+    ~etCameraTest();
+
+    int setUI(const int nLeft, const int nTop, const int nWidth);
+    int widgetHeight();
+    int getCheckStatus();//0:false 1:true 2:未检测
+private slots:
+    void on_btn_IsSelf_clicked();
+    void on_btn_IsNotSelf_clicked();
+
+private:
+    Ui::etCameraTest *ui;
+    std::shared_ptr<QTimer> m_pVideoTimer;
+    std::shared_ptr<QTimer> m_pinitTimer;
+    cv::VideoCapture m_cam;
+	int m_nCameraOpenStatus;
+};
+
+#endif // ETCAMERATEST_H

+ 84 - 0
client/etCameraTest.ui

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>etCameraTest</class>
+ <widget class="QWidget" name="etCameraTest">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>635</width>
+    <height>479</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QWidget" name="widget_etct_camera" native="true">
+   <property name="geometry">
+    <rect>
+     <x>-10</x>
+     <y>0</y>
+     <width>311</width>
+     <height>291</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_HLine">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>340</y>
+     <width>54</width>
+     <height>12</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string/>
+   </property>
+  </widget>
+  <widget class="QToolButton" name="btn_IsSelf">
+   <property name="geometry">
+    <rect>
+     <x>380</x>
+     <y>30</y>
+     <width>191</width>
+     <height>121</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>图像中不是操作者本人</string>
+   </property>
+   <property name="toolButtonStyle">
+    <enum>Qt::ToolButtonTextUnderIcon</enum>
+   </property>
+  </widget>
+  <widget class="QToolButton" name="btn_IsNotSelf">
+   <property name="geometry">
+    <rect>
+     <x>380</x>
+     <y>170</y>
+     <width>191</width>
+     <height>131</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>图像中是操作者本人</string>
+   </property>
+   <property name="toolButtonStyle">
+    <enum>Qt::ToolButtonTextUnderIcon</enum>
+   </property>
+  </widget>
+  <widget class="QTableWidget" name="tablewt_envTest">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>370</y>
+     <width>531</width>
+     <height>281</height>
+    </rect>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 303 - 0
client/etMobileTest.cpp

@@ -0,0 +1,303 @@
+#include "etMobileTest.h"
+#include "ui_etMobileTest.h"
+
+#include <QScrollBar>
+#include <QHBoxLayout>
+#include <QLabel>
+
+#include "CAppInfo.h"
+#include "CQREncode.h"
+#include "awMsgBox.h"
+
+etMobileTest::etMobileTest(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::etMobileTest)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+    m_nUploadStatus = 2;
+
+
+	qRegisterMetaType<CGetWXQrCode>("CGetWXQrCode");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnGetWXQrCode, this, &etMobileTest::onGetWXQrCode);
+	qRegisterMetaType<CSaveUploadedFileAcknowledgeStatus>("CSaveUploadedFileAcknowledgeStatus");
+	connect(g_httpBllPtr.get(), &CHttpBll::sgnSaveUploadedFileAcknowledgeStatus, this, &etMobileTest::onSaveUploadedFileAcknowledgeStatus);
+
+	if (g_websocketPtr == nullptr)
+	{
+		g_websocketPtr = std::make_shared<CWebsocketProc>();
+	}
+	g_websocketPtr->open(QString("wss://%1:%2/api/ws/fileAnswer")
+		.arg(g_appInfoPtr->m_sHttpServer).arg(g_appInfoPtr->m_sHttpPort),
+		g_httpBllPtr->getToken(), g_httpBllPtr->getKey());
+	connect(g_websocketPtr.get(), &CWebsocketProc::mobileStatus, this, &etMobileTest::onMobileStatus);
+	connect(g_websocketPtr.get(), &CWebsocketProc::fileAnswer, this, &etMobileTest::onFileAnswer);
+
+	CHttpRequestPackage hrp;
+	hrp.sUri = "/api/ecs_oe_student/examControl/getQrCode";	
+	hrp.nRequestType = RequestType::rtGetWXQrCode;
+	Json::Value jBody = Json::Value::null;
+	jBody["examRecordDataId"] = g_appInfoPtr->m_oExamInfo.nExamId;
+	jBody["order"] = 1;
+	jBody["transferFileType"] = "AUDIO";
+	jBody["testEnv"] = true;	
+	hrp.sParamList.push_back(QString("CustomBody,%1").arg(jBody.toStyledString().c_str()));
+	hrp.eParamType = HttpParamType::hptCustomBody;
+	g_httpBllPtr->post(hrp);
+
+}
+
+etMobileTest::~etMobileTest()
+{
+	g_websocketPtr->close();
+    delete ui;
+}
+
+int etMobileTest::setUI(const int nLeft, const int nTop, const int nWidth)
+{
+    setGeometry(nLeft, nTop, nWidth, g_appInfoPtr->m_fRate*427);
+    ui->label_etmv_qrcode->setGeometry(g_appInfoPtr->m_fRate*30, g_appInfoPtr->m_fRate*30,
+                                        g_appInfoPtr->m_fRate*220, g_appInfoPtr->m_fRate*220);
+    ui->label_etmt_tips->setGeometry(ui->label_etmv_qrcode->x() + ui->label_etmv_qrcode->width() + g_appInfoPtr->m_fRate*30,
+                                     ui->label_etmv_qrcode->y(), g_appInfoPtr->m_fRate*490, g_appInfoPtr->m_fRate*66);
+    ui->label_etmt_tips->setText(QString::fromLocal8Bit(R"(<html>
+                                         <body>
+                                         <style type="text/css">
+                                         #normalText {
+                                           color: #717171;
+                                           font-size:%1px;
+                                           font-family:Microsoft YaHei;
+                                           text-align: left;
+                                         }
+                                         #hoverText {
+                                           color: #13BA8C;
+                                           font-size:%2px;
+                                           font-family:Microsoft YaHei;
+                                           text-align: left;
+                                         }
+                                        </style>
+
+                                        <span id="normalText">请使用</span>
+                                        <span id="hoverText">微信</span>
+                                        <span id="normalText">扫描二维码后,在微信小程序上录音,并上传文件</span>
+                                         </body>
+                                         </html>)").arg((int)(g_appInfoPtr->m_fRate*24)).arg((int)(g_appInfoPtr->m_fRate*24)));
+    ui->label_HLine->setGeometry(ui->label_etmt_tips->x(), ui->label_etmt_tips->y() + ui->label_etmt_tips->height() + g_appInfoPtr->m_fRate*20,
+                                 g_appInfoPtr->m_fRate*490, g_appInfoPtr->m_fRate*1);
+    ui->label_etmt_status->adjustSize();
+    ui->label_etmt_status->setGeometry(ui->label_etmt_tips->x(), ui->label_HLine->y() + ui->label_HLine->height() + 20,
+                                       g_appInfoPtr->m_fRate*490, ui->label_etmt_status->height());
+    ui->btn_etmt_canotScan->setGeometry(ui->label_etmt_tips->x(), ui->label_etmv_qrcode->y() + ui->label_etmv_qrcode->height() - g_appInfoPtr->m_fRate*40,
+                                        g_appInfoPtr->m_fRate*240, g_appInfoPtr->m_fRate*40);
+    ui->btn_etmt_canotScan->setIconSize(QSize(g_appInfoPtr->m_fRate*13, g_appInfoPtr->m_fRate*13));
+    ui->btn_etmt_canotScan->setIcon(QIcon(":/images/btn-can-not-play"));
+    ui->btn_etmt_uploadFaild->setGeometry(ui->btn_etmt_canotScan->x() + ui->btn_etmt_canotScan->width() + g_appInfoPtr->m_fRate*10,
+                                          ui->btn_etmt_canotScan->y(), ui->btn_etmt_canotScan->width(), ui->btn_etmt_canotScan->height());
+    ui->btn_etmt_uploadFaild->setIconSize(QSize(g_appInfoPtr->m_fRate*13, g_appInfoPtr->m_fRate*13));
+    ui->btn_etmt_uploadFaild->setIcon(QIcon(":/images/btn-can-not-play"));
+    ui->tablewt_envTest->setGeometry(ui->label_etmv_qrcode->x(), ui->label_etmv_qrcode->y() + ui->label_etmv_qrcode->height() + g_appInfoPtr->m_fRate*30,
+                                     width() - g_appInfoPtr->m_fRate*30*2, height() - ui->label_etmv_qrcode->y() - ui->label_etmv_qrcode->height() - g_appInfoPtr->m_fRate*30);
+
+    ui->tablewt_envTest->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablewt_envTest->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablewt_envTest->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablewt_envTest->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablewt_envTest->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablewt_envTest->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablewt_envTest->setRowCount(2);
+    //设置列数
+    ui->tablewt_envTest->setColumnCount(3);
+
+    ui->tablewt_envTest->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablewt_envTest->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+    QStringList sHeadStr;
+    sHeadStr << QString::fromLocal8Bit("检测项") <<
+                QString::fromLocal8Bit("值") <<
+                QString::fromLocal8Bit("状态");
+
+    ui->tablewt_envTest->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablewt_envTest->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablewt_envTest->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablewt_envTest->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    ui->tablewt_envTest->setColumnWidth(0, g_appInfoPtr->m_fRate*525);
+    ui->tablewt_envTest->setColumnWidth(1, g_appInfoPtr->m_fRate*170);
+    ui->tablewt_envTest->setColumnWidth(2, g_appInfoPtr->m_fRate*24);
+    ui->tablewt_envTest->horizontalHeader()->setStretchLastSection(true);
+
+
+    {
+		int i = 0;
+        ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("扫描二维码")));
+        ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("")));
+       /* QWidget *widget = new QWidget;
+        QHBoxLayout *layout = new QHBoxLayout;
+        widget->setLayout(layout);
+        QLabel *icon = new QLabel;
+        icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+        icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+        layout->addWidget(icon);
+        layout->addStretch();
+        ui->tablewt_envTest->setCellWidget(i, 2, widget);*/
+    }
+
+	{
+		int i = 1;
+		ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("上传录音")));
+		ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("")));
+		/*QWidget *widget = new QWidget;
+		QHBoxLayout *layout = new QHBoxLayout;
+		widget->setLayout(layout);
+		QLabel *icon = new QLabel;
+		icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+		icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+		layout->addWidget(icon);
+		layout->addStretch();
+		ui->tablewt_envTest->setCellWidget(i, 2, widget);*/
+	}
+
+    return height();
+}
+
+int etMobileTest::widgetHeight()
+{
+    return height();
+}
+
+void etMobileTest::onGetWXQrCode(CGetWXQrCode getWXQrCode)
+{
+	if (getWXQrCode.nCode == 200)
+	{
+		ui->label_etmv_qrcode->setPixmap(CQREncode::GenerateQRcode(getWXQrCode.sUrl, ui->label_etmv_qrcode->width()));
+	}
+	else
+	{
+        if(getWXQrCode.sMessage.isEmpty())
+        {
+            ShowMsg(QString::fromLocal8Bit("获取二维码失败"), (QWidget*)(this->parent()));
+        }
+        else
+        {
+            ShowMsg(getWXQrCode.sMessage, (QWidget*)(this->parent()));
+        }
+	}
+}
+
+
+void etMobileTest::onMobileStatus(__int64 nExamRecordDataId, int nOrder, QString sScanStatus)
+{
+	if (sScanStatus == "SCANNED")
+	{
+		int i = 0;
+		ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("扫描二维码")));
+		ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("正常")));
+		QWidget *widget = new QWidget;
+		QHBoxLayout *layout = new QHBoxLayout;
+		widget->setLayout(layout);
+		QLabel *icon = new QLabel;
+		icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+		icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+		layout->addWidget(icon);
+		layout->addStretch();
+		ui->tablewt_envTest->setCellWidget(i, 2, widget);
+	}
+}
+
+void etMobileTest::onFileAnswer(__int64 nExamRecordDataId, int nOrder, QString sFileType, QString sFileUrl)
+{
+	CHttpRequestPackage hrp;
+	hrp.sUri = "/api/ecs_oe_student/examControl/saveUploadedFileAcknowledgeStatus";
+	hrp.nRequestType = RequestType::rtSaveUploadedFileAcknowledgeStatus;
+	Json::Value jBody = Json::Value::null;
+	jBody["examRecordDataId"] = nExamRecordDataId;
+	jBody["order"] = nOrder;
+	jBody["filePath"] = sFileUrl.toStdString();
+	jBody["acknowledgeStatus"] = "CONFIRMED";
+	hrp.sParamList.push_back(QString("CustomBody,%1").arg(jBody.toStyledString().c_str()));
+	hrp.eParamType = HttpParamType::hptCustomBody;
+	g_httpBllPtr->post(hrp);
+}
+
+void etMobileTest::onSaveUploadedFileAcknowledgeStatus(CSaveUploadedFileAcknowledgeStatus saveUploadedFileAcknowledgeStatus)
+{
+	if (saveUploadedFileAcknowledgeStatus.nCode == 200)
+	{
+		int i = 1;
+		ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("上传录音")));
+		ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("正常")));
+		QWidget *widget = new QWidget;
+		QHBoxLayout *layout = new QHBoxLayout;
+		widget->setLayout(layout);
+		QLabel *icon = new QLabel;
+		icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+		icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+		layout->addWidget(icon);
+		layout->addStretch();
+		ui->tablewt_envTest->setCellWidget(i, 2, widget);
+        m_nUploadStatus = 1;
+	}
+	else
+	{
+		int i = 1;
+		ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("上传录音")));
+		ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("出错")));
+		QWidget *widget = new QWidget;
+		QHBoxLayout *layout = new QHBoxLayout;
+		widget->setLayout(layout);
+		QLabel *icon = new QLabel;
+		icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+		icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+		layout->addWidget(icon);
+		layout->addStretch();
+		ui->tablewt_envTest->setCellWidget(i, 2, widget);
+	}
+}
+
+void etMobileTest::on_btn_etmt_canotScan_clicked()
+{
+    int i = 0;
+    ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("扫描二维码")));
+    ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("出错")));
+    QWidget *widget = new QWidget;
+    QHBoxLayout *layout = new QHBoxLayout;
+    widget->setLayout(layout);
+    QLabel *icon = new QLabel;
+    icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+    icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+    layout->addWidget(icon);
+    layout->addStretch();
+    ui->tablewt_envTest->setCellWidget(i, 2, widget);
+    m_nUploadStatus = 0;
+}
+
+void etMobileTest::on_btn_etmt_uploadFaild_clicked()
+{
+    int i = 1;
+    ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("上传录音")));
+    ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("出错")));
+    QWidget *widget = new QWidget;
+    QHBoxLayout *layout = new QHBoxLayout;
+    widget->setLayout(layout);
+    QLabel *icon = new QLabel;
+    icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+    icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+    layout->addWidget(icon);
+    layout->addStretch();
+    ui->tablewt_envTest->setCellWidget(i, 2, widget);
+    m_nUploadStatus = 0;
+}
+
+
+int etMobileTest::getCheckStatus()
+{
+    return m_nUploadStatus;
+}

+ 38 - 0
client/etMobileTest.h

@@ -0,0 +1,38 @@
+#ifndef ETMOBILETEST_H
+#define ETMOBILETEST_H
+
+#include <QWidget>
+#include "CHttpBll.h"
+#include "CWebsocketProc.h"
+
+namespace Ui {
+class etMobileTest;
+}
+
+class etMobileTest : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit etMobileTest(QWidget *parent = nullptr);
+    ~etMobileTest();
+
+    int setUI(const int nLeft, const int nTop, const int nWidth);
+    int widgetHeight();
+    int getCheckStatus();//0:false 1:true 2:未检测
+private slots:
+	void onGetWXQrCode(CGetWXQrCode getWXQrCode);
+
+    void onMobileStatus(__int64 nExamRecordDataId, int nOrder, QString sScanStatus);
+    void onFileAnswer(__int64 nExamRecordDataId, int nOrder, QString sFileType, QString sFileUrl);
+	void onSaveUploadedFileAcknowledgeStatus(CSaveUploadedFileAcknowledgeStatus saveUploadedFileAcknowledgeStatus);
+
+    void on_btn_etmt_canotScan_clicked();
+    void on_btn_etmt_uploadFaild_clicked();
+private:
+    Ui::etMobileTest *ui;
+
+    int m_nUploadStatus;
+};
+
+#endif // ETMOBILETEST_H

+ 116 - 0
client/etMobileTest.ui

@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>etMobileTest</class>
+ <widget class="QWidget" name="etMobileTest">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>574</width>
+    <height>467</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QLabel" name="label_etmv_qrcode">
+   <property name="geometry">
+    <rect>
+     <x>60</x>
+     <y>40</y>
+     <width>171</width>
+     <height>151</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string/>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_etmt_tips">
+   <property name="geometry">
+    <rect>
+     <x>290</x>
+     <y>40</y>
+     <width>54</width>
+     <height>12</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>请使用微信扫描二维码后,在微信小程序上录音,并上传文件</string>
+   </property>
+   <property name="wordWrap">
+    <bool>true</bool>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_HLine">
+   <property name="geometry">
+    <rect>
+     <x>280</x>
+     <y>90</y>
+     <width>54</width>
+     <height>12</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string/>
+   </property>
+  </widget>
+  <widget class="QLabel" name="label_etmt_status">
+   <property name="geometry">
+    <rect>
+     <x>260</x>
+     <y>160</y>
+     <width>211</width>
+     <height>16</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>上传文件:未上传文件</string>
+   </property>
+  </widget>
+  <widget class="QToolButton" name="btn_etmt_canotScan">
+   <property name="geometry">
+    <rect>
+     <x>250</x>
+     <y>190</y>
+     <width>75</width>
+     <height>23</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>不能正确扫描二维码</string>
+   </property>
+   <property name="toolButtonStyle">
+    <enum>Qt::ToolButtonTextBesideIcon</enum>
+   </property>
+  </widget>
+  <widget class="QToolButton" name="btn_etmt_uploadFaild">
+   <property name="geometry">
+    <rect>
+     <x>340</x>
+     <y>190</y>
+     <width>75</width>
+     <height>23</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>上传不成功</string>
+   </property>
+   <property name="toolButtonStyle">
+    <enum>Qt::ToolButtonTextBesideIcon</enum>
+   </property>
+  </widget>
+  <widget class="QTableWidget" name="tablewt_envTest">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>250</y>
+     <width>531</width>
+     <height>281</height>
+    </rect>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 234 - 0
client/etNetworkTest.cpp

@@ -0,0 +1,234 @@
+#include "etNetworkTest.h"
+#include "ui_etNetworkTest.h"
+
+#include "CAppInfo.h"
+#include "logproc.h"
+
+#include <QScrollBar>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QProcess>
+#include <regex>
+
+#include "winsock2.h"
+#include "windows.h"
+#include "winsock.h"
+#include "iphlpapi.h"
+
+etNetworkTest::etNetworkTest(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::etNetworkTest)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+	bSpeedPass = false;
+	bDelayPass = false;
+
+	connect(this, &etNetworkTest::checkInfo, this, [&](QString speed, QString sTime) {
+		if (!speed.isEmpty())
+		{
+			if (speed.toInt() > 5)
+			{
+				bSpeedPass = true;
+
+				ui->tablewt_envTest->item(0, 0)->setText(QString::fromLocal8Bit("电脑当前下载速度"));
+				ui->tablewt_envTest->item(0, 1)->setText(speed + "kb/s");
+				QWidget *widget = new QWidget;
+				QHBoxLayout *layout = new QHBoxLayout;
+				widget->setLayout(layout);
+				QLabel *icon = new QLabel;
+				icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+				icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+				layout->addWidget(icon);
+				layout->addStretch();
+				ui->tablewt_envTest->setCellWidget(0, 2, widget);
+			}
+		}
+
+		if(!sTime.isEmpty())
+		{
+			if (sTime.toInt() < 500)
+			{
+				bDelayPass = true;
+			}
+
+			ui->tablewt_envTest->item(1, 0)->setText(QString::fromLocal8Bit("电脑当前网络延迟"));
+			ui->tablewt_envTest->item(1, 1)->setText(sTime+"ms");
+			QWidget *widget = new QWidget;
+			QHBoxLayout *layout = new QHBoxLayout;
+			widget->setLayout(layout);
+			QLabel *icon = new QLabel;
+			icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+			icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+			layout->addWidget(icon);
+			layout->addStretch();
+			ui->tablewt_envTest->setCellWidget(1, 2, widget);
+		}
+
+		if (bSpeedPass && bDelayPass)
+		{
+			m_bIsRun = false;
+		}
+	});
+
+    m_bIsRun = true;
+    m_thread = std::thread(std::bind(&etNetworkTest::threadProc, this));
+}
+
+etNetworkTest::~etNetworkTest()
+{
+    m_bIsRun = false;
+
+    m_thread.join();
+
+    delete ui;
+}
+
+void etNetworkTest::threadProc()
+{
+    PMIB_IFTABLE	m_pTable = NULL;
+    DWORD	 m_dwAdapters = 0;
+    ULONG	 uRetCode = GetIfTable(m_pTable, &m_dwAdapters, TRUE);
+    if (uRetCode == ERROR_NOT_SUPPORTED)
+    {
+        return;
+    }
+
+    if (uRetCode == ERROR_INSUFFICIENT_BUFFER)
+    {
+        m_pTable = (PMIB_IFTABLE)new BYTE[65535];										//假设端口数不超过65535个
+    }
+
+    DWORD	dwLastIn = 0;																			//上一秒钟的接收字节数
+    DWORD	dwLastOut = 0;																			//上一秒钟的发送字节数
+    DWORD	dwBandIn = 0;																			//下载速度
+    DWORD	dwBandOut = 0;
+    while(m_bIsRun)
+    {
+        GetIfTable(m_pTable, &m_dwAdapters, TRUE);
+        DWORD	dwInOctets = 0;
+        DWORD	dwOutOctets = 0;
+
+        //将所有端口的流量进行统计
+        for (UINT i = 0; i < m_pTable->dwNumEntries; i++)
+        {
+            MIB_IFROW	Row = m_pTable->table[i];
+            dwInOctets += Row.dwInOctets;
+            dwOutOctets += Row.dwOutOctets;
+        }
+
+        dwBandIn = dwInOctets - dwLastIn;	//下载速度
+        dwBandOut = dwOutOctets - dwLastOut;	//上传速速
+        if (dwLastIn <= 0)
+        {
+            dwBandIn = 0;
+        }
+        else
+        {
+            dwBandIn = dwBandIn / 1024;	//b转换成kb
+        }
+
+        if (dwLastOut <= 0)
+        {
+            dwBandOut = 0;
+        }
+        else
+        {
+            dwBandOut = dwBandOut / 1024;	//b转换成kb
+        }
+		dwLastIn = dwInOctets;
+		dwLastOut = dwOutOctets;
+
+        QProcess process;
+        process.start("ping www.baidu.com -n 1");
+        process.waitForFinished(-1); // will wait forever until finished
+		QString sOutStr = QString::fromLocal8Bit(process.readAllStandardOutput().data());
+	
+		QString sFindStr = QString::fromLocal8Bit("平均 =");
+		int nIndex = sOutStr.indexOf(sFindStr);
+		if (nIndex < 0)
+		{
+			nIndex = sOutStr.indexOf(QString::fromLocal8Bit("Average ="));
+		}
+
+		QString sTimeNum = "";
+		if (nIndex > 0)
+		{
+			QString sTime = sOutStr.mid(nIndex + sFindStr.length(), sOutStr.length() - nIndex);
+			int nTimeIndex = sTime.indexOf("ms");
+			sTimeNum = sTime.left(nTimeIndex);			
+		}
+		emit checkInfo(QString::number(dwBandIn), sTimeNum);
+		Sleep(1000);
+    }
+}
+
+int etNetworkTest::setUI(const int nLeft, const int nTop, const int nWidth)
+{
+    setGeometry(nLeft, nTop, nWidth, g_appInfoPtr->m_fRate*146);
+    ui->tablewt_envTest->setGeometry(g_appInfoPtr->m_fRate*30, 0, width() - g_appInfoPtr->m_fRate*30*2, height());
+
+    ui->tablewt_envTest->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablewt_envTest->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablewt_envTest->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablewt_envTest->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablewt_envTest->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablewt_envTest->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablewt_envTest->setRowCount(2);
+    //设置列数
+    ui->tablewt_envTest->setColumnCount(3);
+
+    ui->tablewt_envTest->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablewt_envTest->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+    QStringList sHeadStr;
+    sHeadStr << QString::fromLocal8Bit("检测项") <<
+                QString::fromLocal8Bit("值") <<
+                QString::fromLocal8Bit("状态");
+
+    ui->tablewt_envTest->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablewt_envTest->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablewt_envTest->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablewt_envTest->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    ui->tablewt_envTest->setColumnWidth(0, g_appInfoPtr->m_fRate*525);
+    ui->tablewt_envTest->setColumnWidth(1, g_appInfoPtr->m_fRate*170);
+    ui->tablewt_envTest->setColumnWidth(2, g_appInfoPtr->m_fRate*24);
+    ui->tablewt_envTest->horizontalHeader()->setStretchLastSection(true);
+	
+    for(int i = 0; i < 2; i++)
+    {
+        ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("")));
+        ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("")));
+       /* QWidget *widget = new QWidget;
+        QHBoxLayout *layout = new QHBoxLayout;
+        widget->setLayout(layout);
+        QLabel *icon = new QLabel;
+        icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+        icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+        layout->addWidget(icon);
+        layout->addStretch();
+        ui->tablewt_envTest->setCellWidget(i, 2, widget);*/
+    }
+	ui->tablewt_envTest->item(0, 0)->setText(QString::fromLocal8Bit("电脑当前下载速度"));
+	ui->tablewt_envTest->item(1, 0)->setText(QString::fromLocal8Bit("电脑当前网络延迟"));
+	
+    return height();
+}
+
+int etNetworkTest::widgetHeight()
+{
+    return height();
+}
+
+int etNetworkTest::getCheckStatus()
+{
+    return (bSpeedPass && bDelayPass);
+}

+ 35 - 0
client/etNetworkTest.h

@@ -0,0 +1,35 @@
+#ifndef ETNETWORKTEST_H
+#define ETNETWORKTEST_H
+
+#include <QWidget>
+#include <thread>
+#include <mutex>
+
+namespace Ui {
+class etNetworkTest;
+}
+
+class etNetworkTest : public QWidget
+{
+    Q_OBJECT
+signals:
+	void checkInfo(QString speed, QString sTime);	
+public:
+    explicit etNetworkTest(QWidget *parent = nullptr);
+    ~etNetworkTest();
+
+    int setUI(const int nLeft, const int nTop, const int nWidth);
+    int widgetHeight();
+    int getCheckStatus();//0:false 1:true 2:未检测
+private:
+    void threadProc();
+    Ui::etNetworkTest *ui;
+
+    std::thread m_thread;
+    bool m_bIsRun;
+
+	bool bSpeedPass;
+	bool bDelayPass;
+};
+
+#endif // ETNETWORKTEST_H

+ 29 - 0
client/etNetworkTest.ui

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>etNetworkTest</class>
+ <widget class="QWidget" name="etNetworkTest">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>579</width>
+    <height>397</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QTableWidget" name="tablewt_envTest">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>20</y>
+     <width>531</width>
+     <height>281</height>
+    </rect>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 274 - 0
client/etTestResult.cpp

@@ -0,0 +1,274 @@
+#include "etTestResult.h"
+#include "ui_etTestResult.h"
+
+#include "CAppInfo.h"
+
+#include <QScrollBar>
+#include <QHBoxLayout>
+#include <QLabel>
+
+etTestResult::etTestResult(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::etTestResult)
+{
+    ui->setupUi(this);
+
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	ui->tablewt_envTest->setEditTriggers(QAbstractItemView::NoEditTriggers);
+	//整行选中的方式
+	ui->tablewt_envTest->setSelectionBehavior(QAbstractItemView::SelectRows);
+	//设置为只能选中一行
+	ui->tablewt_envTest->setSelectionMode(QAbstractItemView::SingleSelection);
+	//隐藏列表头
+	ui->tablewt_envTest->verticalHeader()->setVisible(false);
+	//隐藏边框
+	ui->tablewt_envTest->setShowGrid(false);
+	//表头不高亮显示
+	ui->tablewt_envTest->horizontalHeader()->setVisible(false);// ->setHighlightSections(false);
+	//设置行数
+	ui->tablewt_envTest->setRowCount(6);
+	//设置列数
+	ui->tablewt_envTest->setColumnCount(2);
+
+	ui->tablewt_envTest->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+	ui->tablewt_envTest->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate * 5);
+
+	QStringList sHeadStr;
+	sHeadStr << QString::fromLocal8Bit("检测项") <<
+		QString::fromLocal8Bit("结果");
+
+	ui->tablewt_envTest->setHorizontalHeaderLabels(sHeadStr);
+	ui->tablewt_envTest->horizontalHeader()->setDefaultAlignment(Qt::AlignJustify | Qt::AlignVCenter);
+	ui->tablewt_envTest->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+	ui->tablewt_envTest->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate * 48);
+	ui->tablewt_envTest->setColumnWidth(0, g_appInfoPtr->m_fRate * 425);
+//	ui->tablewt_envTest->setColumnWidth(1, g_appInfoPtr->m_fRate * 170);
+
+	ui->tablewt_envTest->horizontalHeader()->setStretchLastSection(true);
+
+}
+
+etTestResult::~etTestResult()
+{
+    delete ui;
+}
+
+int etTestResult::setUI(const int nLeft, const int nTop, const int nWidth)
+{
+    setGeometry(nLeft, nTop, nWidth, g_appInfoPtr->m_fRate*146);
+    ui->tablewt_envTest->setGeometry(g_appInfoPtr->m_fRate*30, 0, width() - g_appInfoPtr->m_fRate*30*2, g_appInfoPtr->m_fRate*293);
+     
+	QString sTextStyle = QString(R"(QLabel
+								{
+									font-size:%1px;
+									font-family:"Microsoft YaHei";
+									font-weight:400;
+									color:rgba(153,153,153,1);
+								})").arg((int)(g_appInfoPtr->m_fRate * 12));
+	
+	
+	{
+		int i = 0;
+		QWidget *widget1 = new QWidget;
+		QHBoxLayout *layout1 = new QHBoxLayout;
+		widget1->setLayout(layout1);
+		
+		QLabel *icon1 = new QLabel;
+		icon1->setStyleSheet(sTextStyle);
+		icon1->setText(QString::fromLocal8Bit("检查项"));
+		layout1->addWidget(icon1);
+		layout1->addStretch();
+
+		ui->tablewt_envTest->setCellWidget(i, 0, widget1);
+		
+		QWidget *widget = new QWidget;
+		QHBoxLayout *layout = new QHBoxLayout;
+		widget->setLayout(layout);
+		QLabel *icon = new QLabel;
+		icon->setStyleSheet(sTextStyle);
+		icon->setText(QString::fromLocal8Bit("结果"));
+		layout->addStretch();
+		layout->addWidget(icon);
+		
+
+		ui->tablewt_envTest->setCellWidget(i, 1, widget);
+	}
+
+    ui->lw_ettr_unpass->setGeometry(ui->tablewt_envTest->x(), ui->tablewt_envTest->y() + ui->tablewt_envTest->height() + g_appInfoPtr->m_fRate*20,
+                                    ui->tablewt_envTest->width(), ui->lw_ettr_unpass->count()*g_appInfoPtr->m_fRate*26);
+	setGeometry(nLeft, nTop, nWidth, ui->lw_ettr_unpass->y() + ui->lw_ettr_unpass->height());
+    return height();
+}
+
+int etTestResult::widgetHeight()
+{
+    return ui->lw_ettr_unpass->y() + ui->lw_ettr_unpass->height();
+}
+
+
+void etTestResult::setResult(int nNetWorkStatus, int nTimekStatus, int nCameraStatus, int nVoiceStatus, int nWXStatus)
+{
+	QString sTextStyle = QString(R"(QLabel
+								{
+									font-size:%1px;
+									font-family:"Microsoft YaHei";
+									font-weight:400;
+									color:rgba(254,119,100,1);
+								})").arg((int)(g_appInfoPtr->m_fRate*12));
+    ui->lw_ettr_unpass->clear();
+    ui->lw_ettr_unpass->setIconSize(QSize(g_appInfoPtr->m_fRate*16, g_appInfoPtr->m_fRate*16));
+    int i = 1;
+    {
+        ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("网速")));
+        QWidget *widget = new QWidget;
+        QHBoxLayout *layout = new QHBoxLayout;
+        widget->setLayout(layout);
+        layout->addStretch();
+        QLabel *icon = new QLabel;
+        if(nNetWorkStatus == 1)
+        {
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+            icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+        }
+        else
+        {
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+            icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+            QListWidgetItem *item = new QListWidgetItem(QIcon(QPixmap(":/images/icon-et-tips.png")), QString::fromLocal8Bit("检查网络是否连接,路由器是否正常工作。"));
+            ui->lw_ettr_unpass->addItem(item);
+			item->setSizeHint(QSize(item->sizeHint().width(), g_appInfoPtr->m_fRate*26));
+        }
+        layout->addWidget(icon);
+
+        ui->tablewt_envTest->setCellWidget(i, 1, widget);
+    }
+
+    {
+        i = 2;
+        ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("时钟")));
+        QWidget *widget = new QWidget;
+        QHBoxLayout *layout = new QHBoxLayout;
+        widget->setLayout(layout);
+        layout->addStretch();
+        QLabel *icon = new QLabel;
+        if(nTimekStatus == 1)
+        {
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+            icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+        }
+        else
+        {
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+            icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+            QListWidgetItem *item = new QListWidgetItem(QIcon(QPixmap(":/images/icon-et-tips.png")), QString::fromLocal8Bit("检查电脑时区及时钟是否正常。"));
+            ui->lw_ettr_unpass->addItem(item);
+			item->setSizeHint(QSize(item->sizeHint().width(), g_appInfoPtr->m_fRate * 26));
+        }
+        layout->addWidget(icon);
+
+        ui->tablewt_envTest->setCellWidget(i, 1, widget);
+    }
+
+    {
+        i = 3;
+        ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("摄像头")));
+        QWidget *widget = new QWidget;
+        QHBoxLayout *layout = new QHBoxLayout;
+        widget->setLayout(layout);
+        layout->addStretch();
+        QLabel *icon = new QLabel;
+        if(nCameraStatus == 1)
+        {
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+            icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+        }
+        else if(nCameraStatus == 0)
+        {
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+            icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+            QListWidgetItem *item = new QListWidgetItem(QIcon(QPixmap(":/images/icon-et-tips.png")), QString::fromLocal8Bit("请确认摄像头连接线正常,能正常工作,关闭杀毒软件;请确认您的电脑是否为双摄像头,启用摄像头是否正确。"));
+            ui->lw_ettr_unpass->addItem(item);
+			item->setSizeHint(QSize(item->sizeHint().width(), g_appInfoPtr->m_fRate * 26));
+        }
+        else if(nCameraStatus == 2)
+        {
+			icon->setStyleSheet(sTextStyle);
+            icon->setText(QString::fromLocal8Bit("请在“摄像头”步骤进行人工确认!"));
+            icon->adjustSize();			
+        }
+        layout->addWidget(icon);
+
+        ui->tablewt_envTest->setCellWidget(i, 1, widget);
+    }
+
+    {
+        i = 4;
+        ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("声音")));
+        QWidget *widget = new QWidget;
+        QHBoxLayout *layout = new QHBoxLayout;
+        widget->setLayout(layout);
+        layout->addStretch();
+        QLabel *icon = new QLabel;
+        if(nVoiceStatus == 1)
+        {
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+            icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+        }
+        else if(nVoiceStatus == 0)
+        {
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+            icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+            QListWidgetItem *item = new QListWidgetItem(QIcon(QPixmap(":/images/icon-et-tips.png")), QString::fromLocal8Bit("请确认音箱连接正常,调节音量开关及大小。"));
+            ui->lw_ettr_unpass->addItem(item);
+			item->setSizeHint(QSize(item->sizeHint().width(), g_appInfoPtr->m_fRate * 26));
+        }
+        else if(nVoiceStatus == 2)
+        {
+			icon->setStyleSheet(sTextStyle);
+            icon->setText(QString::fromLocal8Bit("请在“声音”步骤进行人工确认!"));
+            icon->adjustSize();			
+        }
+        layout->addWidget(icon);
+
+        ui->tablewt_envTest->setCellWidget(i, 1, widget);
+    }
+    {
+        i = 5;
+        ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("微信小程序")));
+        QWidget *widget = new QWidget;
+        QHBoxLayout *layout = new QHBoxLayout;
+        widget->setLayout(layout);
+        layout->addStretch();
+        QLabel *icon = new QLabel;
+        if(nWXStatus == 1)
+        {
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+            icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate*12, g_appInfoPtr->m_fRate*9));
+        }
+        else if(nWXStatus == 0)
+        {
+            icon->setFixedSize(QSize(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+            icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate*8, g_appInfoPtr->m_fRate*8));
+            QListWidgetItem *item = new QListWidgetItem(QIcon(QPixmap(":/images/icon-et-tips.png")), QString::fromLocal8Bit("请确认微信已登录并连接网络。"));
+            ui->lw_ettr_unpass->addItem(item);
+			item->setSizeHint(QSize(item->sizeHint().width(), g_appInfoPtr->m_fRate * 26));
+        }
+        else if(nWXStatus == 2)
+        {
+			icon->setStyleSheet(sTextStyle);
+            icon->setText(QString::fromLocal8Bit("请在“微信小程序”步骤进行人工确认!"));
+            icon->adjustSize();			
+        }
+        layout->addWidget(icon);
+
+        ui->tablewt_envTest->setCellWidget(i, 1, widget);
+    }
+
+    if(ui->lw_ettr_unpass->count() > 0)
+    {
+        QListWidgetItem *item = new QListWidgetItem(QIcon(QPixmap(":/images/icon-et-tips.png")), QString::fromLocal8Bit("请按提示检查并调试,调试后可再次进行环境检测。"));
+        ui->lw_ettr_unpass->addItem(item);
+    }
+}

+ 25 - 0
client/etTestResult.h

@@ -0,0 +1,25 @@
+#ifndef ETTESTRESULT_H
+#define ETTESTRESULT_H
+
+#include <QWidget>
+
+namespace Ui {
+class etTestResult;
+}
+
+class etTestResult : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit etTestResult(QWidget *parent = nullptr);
+    ~etTestResult();
+
+    int setUI(const int nLeft, const int nTop, const int nWidth);
+    int widgetHeight();
+    void setResult(int nNetWorkStatus, int nTimekStatus, int nCameraStatus, int nVoiceStatus, int nWXStatus);
+private:
+    Ui::etTestResult *ui;
+};
+
+#endif // ETTESTRESULT_H

+ 39 - 0
client/etTestResult.ui

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>etTestResult</class>
+ <widget class="QWidget" name="etTestResult">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>562</width>
+    <height>446</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <widget class="QTableWidget" name="tablewt_envTest">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>20</y>
+     <width>531</width>
+     <height>221</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QListWidget" name="lw_ettr_unpass">
+   <property name="geometry">
+    <rect>
+     <x>20</x>
+     <y>260</y>
+     <width>521</width>
+     <height>192</height>
+    </rect>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 177 - 0
client/etTimeTest.cpp

@@ -0,0 +1,177 @@
+#include "etTimeTest.h"
+#include "ui_etTimeTest.h"
+
+#include "CAppInfo.h"
+
+#include <QScrollBar>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QTimeZone>
+
+
+
+etTimeTest::etTimeTest(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::etTimeTest)
+{
+    ui->setupUi(this);
+	
+    setStyleSheet(g_appInfoPtr->m_sQssStr);
+
+	bStart = true;
+	
+	QueryPerformanceFrequency(&m_freq);	
+	QueryPerformanceCounter(&m_start_t);
+	m_startTime = QDateTime::currentDateTime();
+	
+	m_pTimer = std::make_shared<QTimer>();
+	m_pTimer->setInterval(5000);
+	connect(m_pTimer.get(), &QTimer::timeout, this, [&]() {
+		m_pTimer->stop();
+		QueryPerformanceCounter(&m_stop_t);
+		m_endTime = QDateTime::currentDateTime();
+		__int64 nMSeconds = m_startTime.msecsTo(m_endTime);
+		__int64 exe_time = 1e3*(m_stop_t.QuadPart - m_start_t.QuadPart) / m_freq.QuadPart;
+		if (abs(nMSeconds - exe_time) < 30)
+		{
+            m_bTime = true;
+			int i = 1;
+
+			ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("电脑时钟频率")));
+			ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("正常")));
+			QWidget *widget = new QWidget;
+			QHBoxLayout *layout = new QHBoxLayout;
+			widget->setLayout(layout);
+			QLabel *icon = new QLabel;
+			icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+			icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+			layout->addWidget(icon);
+			layout->addStretch();
+			ui->tablewt_envTest->setCellWidget(i, 2, widget);
+		}
+		else
+		{
+            m_bTime = false;
+
+			int i = 1;
+
+			ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("电脑时钟频率")));
+			ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("异常")));
+			QWidget *widget = new QWidget;
+			QHBoxLayout *layout = new QHBoxLayout;
+			widget->setLayout(layout);
+			QLabel *icon = new QLabel;
+			icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+			icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 8));
+			layout->addWidget(icon);
+			layout->addStretch();
+			ui->tablewt_envTest->setCellWidget(i, 2, widget);
+		}
+	});
+
+	m_pTimer->start();
+}
+
+etTimeTest::~etTimeTest()
+{
+    delete ui;
+}
+
+int etTimeTest::setUI(const int nLeft, const int nTop, const int nWidth)
+{
+    setGeometry(nLeft, nTop, nWidth, g_appInfoPtr->m_fRate*146);
+    ui->tablewt_envTest->setGeometry(g_appInfoPtr->m_fRate*30, 0, width() - g_appInfoPtr->m_fRate*30*2, height());
+
+    ui->tablewt_envTest->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    //整行选中的方式
+    ui->tablewt_envTest->setSelectionBehavior(QAbstractItemView::SelectRows);
+    //设置为只能选中一行
+    ui->tablewt_envTest->setSelectionMode(QAbstractItemView::SingleSelection);
+    //隐藏列表头
+    ui->tablewt_envTest->verticalHeader()->setVisible(false);
+    //隐藏边框
+    ui->tablewt_envTest->setShowGrid(false);
+    //表头不高亮显示
+    ui->tablewt_envTest->horizontalHeader()->setHighlightSections(false);
+    //设置行数
+    ui->tablewt_envTest->setRowCount(2);
+    //设置列数
+    ui->tablewt_envTest->setColumnCount(3);
+
+    ui->tablewt_envTest->setHorizontalScrollMode(QTableWidget::ScrollPerPixel);
+    ui->tablewt_envTest->horizontalScrollBar()->setSingleStep(g_appInfoPtr->m_fRate*5);
+
+    QStringList sHeadStr;
+    sHeadStr << QString::fromLocal8Bit("检测项") <<
+                QString::fromLocal8Bit("值") <<
+                QString::fromLocal8Bit("状态");
+
+    ui->tablewt_envTest->setHorizontalHeaderLabels(sHeadStr);
+    ui->tablewt_envTest->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    ui->tablewt_envTest->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
+
+    ui->tablewt_envTest->verticalHeader()->setDefaultSectionSize(g_appInfoPtr->m_fRate*48);
+    ui->tablewt_envTest->setColumnWidth(0, g_appInfoPtr->m_fRate*525);
+    ui->tablewt_envTest->setColumnWidth(1, g_appInfoPtr->m_fRate*170);
+    ui->tablewt_envTest->setColumnWidth(2, g_appInfoPtr->m_fRate*24);
+    ui->tablewt_envTest->horizontalHeader()->setStretchLastSection(true);
+
+	{
+		int i = 0;
+		QDateTime currentTime = QDateTime::currentDateTime();
+		QTimeZone currentZone = currentTime.timeZone();
+		QString currentZoneName = currentZone.displayName(QTimeZone::StandardTime, QTimeZone::OffsetName);//当前时间的当前时区
+		const int LOCAL_TIME_ZONE = 24 - QDateTime(QDate(1970, 1, 2), QTime(0, 0, 0), Qt::LocalTime).toTime_t() / 3600;
+		ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("电脑时区")));
+		ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(currentZoneName));
+		QWidget *widget = new QWidget;
+		QHBoxLayout *layout = new QHBoxLayout;
+		widget->setLayout(layout);
+		QLabel *icon = new QLabel;
+		icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+		if(LOCAL_TIME_ZONE == 8)
+		{ 
+            m_bTimeZone = true;
+			icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+		}
+		else
+		{
+            m_bTimeZone = false;
+			icon->setPixmap(QPixmap(":/images/icon-et-iten-unpass.png").scaled(g_appInfoPtr->m_fRate * 8, g_appInfoPtr->m_fRate * 4));
+		}
+		
+		layout->addWidget(icon);
+		layout->addStretch();
+		ui->tablewt_envTest->setCellWidget(i, 2, widget);
+	}
+	/*
+	{
+		int i = 1;
+		
+		ui->tablewt_envTest->setItem(i, 0, new QTableWidgetItem(QString::fromLocal8Bit("电脑时钟频率")));
+		ui->tablewt_envTest->setItem(i, 1, new QTableWidgetItem(QString::fromLocal8Bit("正常")));
+		QWidget *widget = new QWidget;
+		QHBoxLayout *layout = new QHBoxLayout;
+		widget->setLayout(layout);
+		QLabel *icon = new QLabel;
+		icon->setFixedSize(QSize(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+		icon->setPixmap(QPixmap(":/images/icon-et-item-pass.png").scaled(g_appInfoPtr->m_fRate * 12, g_appInfoPtr->m_fRate * 9));
+		layout->addWidget(icon);
+		layout->addStretch();
+		ui->tablewt_envTest->setCellWidget(i, 2, widget);
+	}*/
+	
+    return height();
+}
+
+int etTimeTest::widgetHeight()
+{
+    return height();
+}
+
+
+int etTimeTest::getCheckStatus()
+{
+    return (m_bTime && m_bTimeZone);
+}
+

部分文件因文件數量過多而無法顯示