Переглянути джерело

V0.9.12
1、合并了冲突

Apple 6 днів тому
батько
коміт
c1b5f5afde
82 змінених файлів з 4041 додано та 387 видалено
  1. 1 1
      Libraries/LHHTTPAPI/FindLHHTTPAPI.cmake
  2. BIN
      Libraries/LHHTTPAPI/lib/MinGW_64/LHSqlWebInterface.dll
  3. BIN
      Libraries/LHHTTPAPI/lib/MinGW_64/LHSqlWebInterfaced.dll
  4. BIN
      Libraries/LHHTTPAPI/lib/MinGW_64_Qt5.15.2/LHSqlWebInterface.dll
  5. BIN
      Libraries/LHHTTPAPI/lib/MinGW_64_Qt5.15.2/LHSqlWebInterfaced.dll
  6. 6 1
      Libraries/Libraries.cmake
  7. 22 17
      Libraries/qmqtt/Findqmqtt.cmake
  8. 38 21
      Libraries/spdlog/Findspdlog.cmake
  9. BIN
      Libraries/spdlog/windows/gcc11.2/gcc11.2_Qt6.zip
  10. BIN
      Libraries/spdlog/windows/gcc11.2/libspdlog.a
  11. BIN
      Libraries/spdlog/windows/gcc11.2/libspdlog.dll
  12. BIN
      Libraries/spdlog/windows/gcc11.2/libspdlog.dll.a
  13. BIN
      Libraries/spdlog/windows/gcc11.2/libspdlogd.a
  14. BIN
      Libraries/spdlog/windows/gcc11.2/libspdlogd.dll
  15. BIN
      Libraries/spdlog/windows/gcc11.2/libspdlogd.dll.a
  16. 19 0
      common/DateSelect/CMakeLists.txt
  17. 89 0
      common/DateSelect/calendardtedit.cpp
  18. 35 0
      common/DateSelect/calendardtedit.h
  19. 187 0
      common/DateSelect/calendarex.cpp
  20. 66 0
      common/DateSelect/calendarex.h
  21. 67 0
      common/DateSelect/calendarheader.cpp
  22. 32 0
      common/DateSelect/calendarheader.h
  23. 139 0
      common/DateSelect/calendarheader.ui
  24. 94 0
      common/DateSelect/calendarnav.cpp
  25. 33 0
      common/DateSelect/calendarnav.h
  26. 140 0
      common/DateSelect/calendarnav.ui
  27. 408 0
      common/DateSelect/calendarwidgetex.cpp
  28. 117 0
      common/DateSelect/calendarwidgetex.h
  29. 131 0
      common/DateSelect/cdate.cpp
  30. 63 0
      common/DateSelect/cdate.h
  31. BIN
      common/DateSelect/icon/dark/nextMonth.png
  32. BIN
      common/DateSelect/icon/dark/nextMonth_hover.png
  33. BIN
      common/DateSelect/icon/dark/nextYear.png
  34. BIN
      common/DateSelect/icon/dark/nextYear_hover.png
  35. BIN
      common/DateSelect/icon/dark/preMonth.png
  36. BIN
      common/DateSelect/icon/dark/preMonth_hover.png
  37. BIN
      common/DateSelect/icon/dark/preYear.png
  38. BIN
      common/DateSelect/icon/dark/preYear_hover.png
  39. BIN
      common/DateSelect/icon/light/nextMonth.png
  40. BIN
      common/DateSelect/icon/light/nextMonth_hover.png
  41. BIN
      common/DateSelect/icon/light/nextYear.png
  42. BIN
      common/DateSelect/icon/light/nextYear_hover.png
  43. BIN
      common/DateSelect/icon/light/preMonth.png
  44. BIN
      common/DateSelect/icon/light/preMonth_hover.png
  45. BIN
      common/DateSelect/icon/light/preYear.png
  46. BIN
      common/DateSelect/icon/light/preYear_hover.png
  47. 64 0
      common/DateSelect/qss/CalendarNav_dark.qss
  48. 50 0
      common/DateSelect/qss/CalendarNav_light.qss
  49. 6 0
      common/DateSelect/qss/calendarheader_dark.qss
  50. 6 0
      common/DateSelect/qss/calendarheader_light.qss
  51. 24 0
      common/DateSelect/rescalendar.qrc
  52. 63 0
      common/DateSelect/scopecalendarbtn.cpp
  53. 33 0
      common/DateSelect/scopecalendarbtn.h
  54. 34 0
      common/DateSelect/scopeselectionmodel.cpp
  55. 26 0
      common/DateSelect/scopeselectionmodel.h
  56. 232 0
      common/FlowLayout/flowlayout.cpp
  57. 91 0
      common/FlowLayout/flowlayout.h
  58. 1 0
      common/FlowLayout/说明.txt
  59. 0 7
      common/Shadow/OneShadowEffect.h
  60. 54 0
      common/TimeSelect/QSS/timepartwidget.qss
  61. 12 0
      common/TimeSelect/QSS/timewidget.qss
  62. 25 0
      common/TimeSelect/mytimedelegate.cpp
  63. 16 0
      common/TimeSelect/mytimedelegate.h
  64. 40 0
      common/TimeSelect/shadowwidget.cpp
  65. 20 0
      common/TimeSelect/shadowwidget.h
  66. 177 0
      common/TimeSelect/timepartwidget.cpp
  67. 52 0
      common/TimeSelect/timepartwidget.h
  68. 61 0
      common/TimeSelect/timepartwidget.ui
  69. 502 0
      common/TimeSelect/timewidget.cpp
  70. 112 0
      common/TimeSelect/timewidget.h
  71. 81 0
      common/TimeSelect/timewidget.ui
  72. 2 1
      common/TipWidget/tipwidget.cpp
  73. 1 0
      common/TipWidget/tipwidget.h
  74. 101 0
      common/UIStyle/UIStyleManager.cpp
  75. 73 0
      common/UIStyle/UIStyleManager.h
  76. 1 1
      common/combox/customcombobox.cpp
  77. 0 205
      common/warning/warning copy.ui_
  78. 100 9
      common/warning/warning.cpp
  79. 15 2
      common/warning/warning.h
  80. 37 122
      common/warning/warning.ui
  81. 122 0
      common/warning/warning_dark.qss
  82. 120 0
      common/warning/warning_light.qss

+ 1 - 1
Libraries/LHHTTPAPI/FindLHHTTPAPI.cmake

@@ -60,7 +60,7 @@ endif()
 
 
 #定义变量
-if(LHHTTPAPI_INCLUDE_DIRS AND LHHTTPAPI_SOURCE_DIRS AND LHHTTPAPI_LIBRARY)
+if(LHHTTPAPI_INCLUDE_DIRS AND LHHTTPAPI_SOURCE_DIRS)
     set(LHHTTPAPI_FOUND TRUE)
     set(LHHTTPAPI_VERSION 1.0.9)
 

BIN
Libraries/LHHTTPAPI/lib/MinGW_64/LHSqlWebInterface.dll


BIN
Libraries/LHHTTPAPI/lib/MinGW_64/LHSqlWebInterfaced.dll


BIN
Libraries/LHHTTPAPI/lib/MinGW_64_Qt5.15.2/LHSqlWebInterface.dll


BIN
Libraries/LHHTTPAPI/lib/MinGW_64_Qt5.15.2/LHSqlWebInterfaced.dll


+ 6 - 1
Libraries/Libraries.cmake

@@ -20,9 +20,14 @@ if(Qt5_FOUND)
 elseif(Qt6_FOUND)
     message(STATUS "Qt 版本: ${Qt6_VERSION}")
 else()
-    message(STATUS "Qt 版本: 无")
+    message(STATUS "Qt 版本: Unknown")
+    message(STATUS "如果库依赖Qt版本,将include(Libraries.cmake)放在find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)后面")
 endif()
 
+message(STATUS " ")
+message(STATUS "如果添加的是源文件,需要在路径后面添加/*.cpp,如:")
+message(STATUS "\${LHHTTPAPI_INCLUDE_DIRS}/*.cpp)")
+
 
 message(STATUS "==================================================")
 

+ 22 - 17
Libraries/qmqtt/Findqmqtt.cmake

@@ -4,7 +4,7 @@
 message(STATUS "***** Find qmqtt.cmake *****")
 
 # 0. 定义qmqtt版本
-set(qmqtt_VERSION 1.0.3)
+
 
 # 1. 指定头文件路径
 find_path(qmqtt_INCLUDE_DIR
@@ -15,49 +15,50 @@ find_path(qmqtt_INCLUDE_DIR
 
 
 #区分Qt版本
-if(Qt5Core_FOUND)
-    # message(STATUS "Qt Version : ${Qt5Core_VERSION}")
+if(QT_VERSION_MAJOR EQUAL 5)
+    # message(STATUS "----------------Qt Version : ${Qt5Core_VERSION}")
     if(${Qt5Core_VERSION} VERSION_LESS 5.15.0 AND ${Qt5Core_VERSION} VERSION_GREATER 5.12.0)
         set(QtVersion 5.12)
     elseif(${Qt5Core_VERSION} VERSION_GREATER_EQUAL 5.15.0)
         set(QtVersion 5.15)
     endif()
-elseif(Qt6Core_FOUND)
+elseif(QT_VERSION_MAJOR EQUAL 6)
     message(STATUS "Qt Version : ${Qt6Core_VERSION}")
     message(STATUS "qmqtt not support Qt6")
 endif()
 
+
 # 2. 直接指定库文件,区分gcc,msvc,debug和release
+unset(qmqtt_LIBRARY CACHE)
 if(CMAKE_SYSTEM_NAME MATCHES "Windows")
-
     if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
         if(QtVersion STREQUAL "5.12")
             if(CMAKE_BUILD_TYPE STREQUAL "Debug")
-                set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/gcc/5.12.12/Qt5Qmqttd.dll)
+                list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/gcc/5.12.12/Qt5Qmqttd.dll)
             else()
-                set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/gcc/5.12.12/Qt5Qmqtt.dll)
+                list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/gcc/5.12.12/Qt5Qmqtt.dll)
             endif()
         elseif(QtVersion STREQUAL "5.15")
             if(CMAKE_BUILD_TYPE STREQUAL "Debug")
-                set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/gcc/5.15.2/debug/Qt5Qmqtt.dll)
+                list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/gcc/5.15.2/debug/Qt5Qmqtt.dll)
             else()
-                set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/gcc/5.15.2/release/Qt5Qmqtt.dll)
+                list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/gcc/5.15.2/release/Qt5Qmqtt.dll)
             endif()
         endif()
     elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
         # message(STATUS "MSVC")
         if(QtVersion STREQUAL "5.12")
             if(CMAKE_BUILD_TYPE STREQUAL "Debug")
-                set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/msvc/5.12.12/Qt5Qmqttd.lib)
+                list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/msvc/5.12.12/Qt5Qmqttd.lib)
             else()
-                set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/msvc/5.12.12/Qt5Qmqtt.lib)
+                list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/msvc/5.12.12/Qt5Qmqtt.lib)
             endif(CMAKE_BUILD_TYPE STREQUAL "Debug")
             
         elseif(QtVersion STREQUAL "5.15")
             if(CMAKE_BUILD_TYPE STREQUAL "Debug")
-                set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/msvc/5.15.2/Qt5Qmqttd.lib)
+                list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/msvc/5.15.2/Qt5Qmqttd.lib)
             else()
-                set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/msvc/5.15.2/Qt5Qmqtt.lib)
+                list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/msvc/5.15.2/Qt5Qmqtt.lib)
             endif()
         endif()
     endif()
@@ -66,22 +67,26 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
     # message(STATUS "Using ${CMAKE_SYSTEM_NAME} System")
     if(QtVersion STREQUAL "5.12")
         if(CMAKE_BUILD_TYPE STREQUAL "Debug")
-            set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/linux_gcc8.3/5.12.12/debug/libQt5Qmqtt.so)
+            list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/linux_gcc8.3/5.12.12/debug/libQt5Qmqtt.so)
         else()
-            set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/linux_gcc8.3/5.12.12/release/libQt5Qmqtt.so)
+            list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/linux_gcc8.3/5.12.12/release/libQt5Qmqtt.so)
         endif()
     elseif(QtVersion STREQUAL "5.15")
         if(CMAKE_BUILD_TYPE STREQUAL "Debug")
-            set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/linux_gcc8.3/5.15.14/debug/libQt5Qmqtt.so)
+            list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/linux_gcc8.3/5.15.14/debug/libQt5Qmqtt.so)
         else()
-            set(qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/linux_gcc8.3/5.15.14/debug/libQt5Qmqtt.so)
+            list(APPEND qmqtt_LIBRARY ${CMAKE_CURRENT_LIST_DIR}/lib/linux_gcc8.3/5.15.14/debug/libQt5Qmqtt.so)
         endif()
     endif()
 endif()
 
+
+# message("Include: " ${qmqtt_INCLUDE_DIR})
+# message("Library: " ${qmqtt_LIBRARY})
 # 3. 设置查找到了变量
 if(qmqtt_INCLUDE_DIR AND qmqtt_LIBRARY)
     set(qmqtt_FOUND TRUE)
+    set(qmqtt_VERSION 1.0.3)
 
     # message(STATUS "qmqtt Version : ${qmqtt_VERSION}")
     message(STATUS "include : qmqtt_INCLUDE_DIR")

+ 38 - 21
Libraries/spdlog/Findspdlog.cmake

@@ -1,31 +1,46 @@
 
 
-#SPDLOG日志库,Qt5使用1.14.1,Qt6使用1.15.1,不包含Qt则使用1.15.1
-#Qt5使用1.15.1会报错,Qt6使用1.14.1会报错,不知道为何
+# SPDLOG库分为1.14.1和1.15.1,1.14.1是使用Qt5.12.12编译的,只能使用在Qt5环境中
+# 1.15.1是没有带Qt的环境编译的,支持Qt5和Qt6,编译器版本是GCC11.3
+# 静态库好像不能跨GCC版本使用,链接时会报错,提示找不到符号
 
 message(STATUS "***** Find spdlog Library *****")
 
-#设置版本号
-if(Qt5_FOUND)
-    set(SPDLOG_V 1.14.1)
-elseif(Qt6_FOUND)
-    set(SPDLOG_V 1.15.1)
-else()
-    set(SPDLOG_V 1.15.1)
-endif()
 
-#寻找头文件,现在需要区分Qt版本了,Qt5使用1.14.1,Qt6使用1.15.1,不包含Qt则使用1.15.1
+#寻找头文件,需要判断gcc编译器版本
 unset(spdlog_INCLUDE_DIR CACHE)
 unset(SPDLOG_INC_DIR CACHE)
-
-if(Qt5_FOUND)
+#判断操作系统
+if(CMAKE_SYSTEM_NAME MATCHES "Windows")
+    if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+        if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.3)
+            message(STATUS "GCC编译器版本过低,请使用GCC7.3及以上版本")
+            return()
+        elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.2 AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.2)
+            set(SPDLOG_V 1.14.1)
+            set(SPDLOG_INC_DIR ${CMAKE_CURRENT_LIST_DIR}/include_1.14.1)
+        elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 11.1)
+            set(SPDLOG_V 1.15.1)
+            set(SPDLOG_INC_DIR ${CMAKE_CURRENT_LIST_DIR}/include_1.15.1)
+        endif()
+    elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
+        #MSVC编译器
+        set(SPDLOG_V 1.14.1)
+        set(SPDLOG_INC_DIR ${CMAKE_CURRENT_LIST_DIR}/include_1.14.1)
+    endif()
+    
+elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
+    set(SPDLOG_V 1.14.1)
+    set(SPDLOG_INC_DIR ${CMAKE_CURRENT_LIST_DIR}/include_1.14.1)
+elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
+    set(SPDLOG_V 1.14.1)
     set(SPDLOG_INC_DIR ${CMAKE_CURRENT_LIST_DIR}/include_1.14.1)
-elseif(Qt6_FOUND)
-    set(SPDLOG_INC_DIR ${CMAKE_CURRENT_LIST_DIR}/include_1.15.1)
 else()
-    set(SPDLOG_INC_DIR ${CMAKE_CURRENT_LIST_DIR}/include_1.15.1)
+    message(STATUS "操作系统:未知")
 endif()
 
+
+
 list(APPEND spdlog_INCLUDE_DIR ${SPDLOG_INC_DIR})
 list(APPEND spdlog_INCLUDE_DIR ${SPDLOG_INC_DIR}/spdlog)
 #包含fmt头文件,使用fmt的时候包含fmt/bundled/format.h这样的完整目录
@@ -42,13 +57,13 @@ if(CMAKE_SYSTEM_NAME MATCHES "Windows")
     if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
         #64位
         if(CMAKE_SIZEOF_VOID_P EQUAL 8)
-            #Qt5版本
-            if(Qt5_FOUND)
+            #GCC7.3版本
+            if(SPDLOG_V EQUAL 1.14.1)
                 set(SPDLOG_LIB_DIR ${CMAKE_CURRENT_LIST_DIR}/windows/gcc7.3)
-            #Qt6版本
-            elseif(Qt6_FOUND)
+            #GCC11.2版本
+            elseif(SPDLOG_V EQUAL 1.15.1)
                 set(SPDLOG_LIB_DIR ${CMAKE_CURRENT_LIST_DIR}/windows/gcc11.2)
-            #没有Qt版本
+            #默认版本
             else()
                 set(SPDLOG_LIB_DIR ${CMAKE_CURRENT_LIST_DIR}/windows/gcc11.2)
             endif()
@@ -128,6 +143,8 @@ endif()
 # endif()
 
 # message(STATUS "spdlog_INCLUDE_DIR: ${spdlog_INCLUDE_DIR}")
+# message(STATUS "spdlog_LIBRARY: ${spdlog_LIBRARY}")
+# message(STATUS "spdlog_STATIC_LIBRARY: ${spdlog_STATIC_LIBRARY}")
 
 if(spdlog_INCLUDE_DIR AND (spdlog_LIBRARY OR spdlog_STATIC_LIBRARY))
     set(spdlog_FOUND TRUE)

BIN
Libraries/spdlog/windows/gcc11.2/gcc11.2_Qt6.zip


BIN
Libraries/spdlog/windows/gcc11.2/libspdlog.a


BIN
Libraries/spdlog/windows/gcc11.2/libspdlog.dll


BIN
Libraries/spdlog/windows/gcc11.2/libspdlog.dll.a


BIN
Libraries/spdlog/windows/gcc11.2/libspdlogd.a


BIN
Libraries/spdlog/windows/gcc11.2/libspdlogd.dll


BIN
Libraries/spdlog/windows/gcc11.2/libspdlogd.dll.a


+ 19 - 0
common/DateSelect/CMakeLists.txt

@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 3.10)
+project(CDate)
+
+set(LIB_NAME CDate)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake)
+
+file(GLOB SRC ${COMMON_SRC}
+            ${CMAKE_CURRENT_SOURCE_DIR}/*.qss
+            ${CMAKE_CURRENT_SOURCE_DIR}/date/*.qrc
+            ${CMAKE_CURRENT_SOURCE_DIR}/date/*.cpp
+            ${CMAKE_CURRENT_SOURCE_DIR}/date/*.ui
+        )
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/date
+                    ${COMMON_INC})
+set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
+#add_library(${LIB_NAME} SHARED ${SRC})
+#target_link_libraries(${LIB_NAME} PRIVATE Qt5::Widgets)

+ 89 - 0
common/DateSelect/calendardtedit.cpp

@@ -0,0 +1,89 @@
+#include "calendardtedit.h"
+#include <QDebug>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QKeyEvent>
+#include <QEvent>
+
+#include "calendarex.h"
+
+CalendarDTEdit::CalendarDTEdit(QWidget *parent) : QDateTimeEdit(parent)
+{
+    setCalendarPopup(false);// 阻止自带的日历窗口
+}
+
+CalendarDTEdit::~CalendarDTEdit()
+{
+}
+
+void CalendarDTEdit::CloseCalendar()
+{
+
+}
+
+/* 手动触发日期选择弹框 */
+void CalendarDTEdit::triggerCalendarPopup()
+{
+
+}
+
+/**
+ * @brief 设置手动禁止修改日期区域,只能使用弹窗
+ * 
+ * @param value 是否禁止修改日期区域
+ * @param triggerPopup 点击日期区域是否会跳出弹窗
+ */
+void CalendarDTEdit::setManualDisableEdit(bool value, bool triggerPopup)
+{
+    if(value)
+    {
+        setReadOnly(true);
+        for(auto& it : children())
+        {
+            if(it->objectName() == "qt_spinbox_lineedit")
+            {
+                m_pCalendar = it;
+                m_pCalendar->installEventFilter(this);
+            }
+        }
+    }else {
+        setReadOnly(false);
+        if(m_pCalendar != nullptr)
+        {
+            m_pCalendar->removeEventFilter(this);
+            m_pCalendar = nullptr;
+        }
+    }
+    m_isPopup = triggerPopup;
+}
+
+void CalendarDTEdit::mousePressEvent(QMouseEvent *e)
+{
+    Q_UNUSED(e);
+    CalendarEx *pC = new CalendarEx(date());
+    if (nullptr == pC) return;
+    connect(pC, &CalendarEx::sig_DateChanged, this, [this](const QDate& date){
+        if (date.isValid()) {
+            setDate(date);
+        }
+    });
+    pC->show();
+    pC->positionCalendarPopup(this);
+}
+
+
+bool CalendarDTEdit::eventFilter(QObject* watched, QEvent* event)
+{
+    if(watched == m_pCalendar)
+    {
+        if(event->type() == QEvent::MouseButtonPress)
+        {
+            // qDebug() << "点击了日期区域";
+            if(m_isPopup)
+            {
+                mousePressEvent(nullptr);
+            }
+        }
+    }
+    return QDateTimeEdit::eventFilter(watched, event);
+}

+ 35 - 0
common/DateSelect/calendardtedit.h

@@ -0,0 +1,35 @@
+#ifndef CALENDARDTEDIT_H
+#define CALENDARDTEDIT_H
+
+#include <QWidget>
+#include <QDateTimeEdit>
+
+
+/* 需要设置这个属性,qss设置的图标才会生效
+ * setCalendarPopup(true);
+ */
+
+class CalendarDTEdit : public QDateTimeEdit
+{
+    Q_OBJECT
+public:
+    explicit CalendarDTEdit(QWidget *parent = nullptr);
+    virtual ~CalendarDTEdit();
+    void SetCalendarAutoClose(bool value);
+    void CloseCalendar();
+    /* 手动触发日期选择弹框 */
+    void triggerCalendarPopup();
+    /* 设置手动禁止修改日期区域,只能使用弹窗,第二个参数设置点击日期是否会出现日期选择弹框 */
+    void setManualDisableEdit(bool value, bool triggerPopup = false);
+signals:
+    void sig_SetCurrentPage(int year, int month);
+protected:
+    void mousePressEvent(QMouseEvent* e) override;
+    bool eventFilter(QObject* watched, QEvent* event) override;
+
+private:
+    QObject* m_pCalendar = nullptr;
+    bool m_isPopup = false;
+};
+
+#endif // CALENDARDTEDIT_H

+ 187 - 0
common/DateSelect/calendarex.cpp

@@ -0,0 +1,187 @@
+#include "calendarex.h"
+#include <QEvent>
+#include <QScreen>
+#include <QKeyEvent>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QHBoxLayout>
+#include <QDebug>
+#include "scopeselectionmodel.h"
+#include "PaintHelper/painthelper.h"
+
+CalendarInterface::CalendarInterface(QWidget *parent)
+    : QWidget(parent)
+    , IDropShadowable(this)
+    , m_pLayout(new QHBoxLayout)
+{
+    setWindowFlag(Qt::Popup);
+    setWindowFlag(Qt::NoDropShadowWindowHint);
+    //设置无边框属性
+    setWindowFlag(Qt::FramelessWindowHint);
+    //设置背景透明属性
+    setAttribute(Qt::WA_TranslucentBackground, true);
+    //关闭对话框时,删除自身对象
+    setAttribute(Qt::WA_DeleteOnClose, true);
+}
+
+CalendarInterface::~CalendarInterface()
+{
+    //qDebug()<<"~ScopeCalendar";
+}
+
+/**
+ * @brief 这个只是坐标位移?
+ * @param q
+ */
+void CalendarInterface::positionCalendarPopup(QWidget *q)
+{
+    if (nullptr == m_pLayout) return;
+    QPoint pos = (q->layoutDirection() == Qt::RightToLeft) ? q->rect().bottomRight() : q->rect().bottomLeft();
+    QPoint pos2 = (q->layoutDirection() == Qt::RightToLeft) ? q->rect().topRight() : q->rect().topLeft();
+    pos = q->mapToGlobal(pos);
+    pos2 = q->mapToGlobal(pos2);
+    QSize size = QSize(CALENDAR_WIDTH * m_pLayout->count() + 2 * SHADOW_RADIUS, CALENDAR_HEIGHT + 2 * SHADOW_RADIUS);
+    //QRect screen = QApplication::desktop()->availableGeometry(pos);
+    QRect screen = QGuiApplication::screenAt(pos)->availableGeometry();
+    //handle popup falling "off screen"
+    if (q->layoutDirection() == Qt::RightToLeft) {
+        pos.setX(pos.x()-size.width());
+        pos2.setX(pos2.x()-size.width());
+        if (pos.x() < screen.left())
+            pos.setX(qMax(pos.x(), screen.left()));
+        else if (pos.x()+size.width() > screen.right())
+            pos.setX(qMax(pos.x()-size.width(), screen.right()-size.width()));
+    } else {
+        if (pos.x()+size.width() > screen.right())
+            pos.setX(screen.right()-size.width());
+        pos.setX(qMax(pos.x(), screen.left()));
+    }
+    if (pos.y() + size.height() > screen.bottom())
+        pos.setY(pos2.y() - size.height());
+    else if (pos.y() < screen.top())
+        pos.setY(screen.top());
+    if (pos.y() < screen.top())
+        pos.setY(screen.top());
+    if (pos.y()+size.height() > screen.bottom())
+        pos.setY(screen.bottom()-size.height());
+    pos.setX(pos.rx() - SHADOW_RADIUS);
+    pos.setY(pos.ry() - SHADOW_RADIUS + SPACING);
+    move(pos);
+}
+
+CalendarEx::CalendarEx(const QDate& defaultDate, QWidget* parent) :
+    CalendarInterface(parent),
+    m_pCalendar(nullptr)
+{
+    QHBoxLayout* pLay = new QHBoxLayout(this);
+    QWidget* pMain = new QWidget(this);
+    pMain->setLayout(m_pLayout);
+
+    pLay->addWidget(pMain);
+    pLay->setMargin(SHADOW_RADIUS);
+
+    m_pCalendar = new CalendarWidgetEx(this);
+    m_pCalendar->setStyleSheet("QCalendarView{background-color: transparent;}");
+    m_pLayout->addWidget(m_pCalendar);
+    connect(m_pCalendar, &CalendarWidgetEx::clicked, this, [this](const QDate& date){
+        emit sig_DateChanged(date);
+        close();
+    });
+
+    m_pLayout->setMargin(0);
+    pMain->resize(CALENDAR_WIDTH, CALENDAR_HEIGHT);
+    SetCalendarSync(defaultDate);
+    /* 设置大小,包含阴影的大小 */
+    resize(CALENDAR_WIDTH + 2 * SHADOW_RADIUS, CALENDAR_HEIGHT + 2 * SHADOW_RADIUS);
+    /* 设置阴影 */
+    SetDropShadow(BoxShadow{0, 0, SHADOW_RADIUS, 0, QColor(0, 0, 0, 60), QSize(0, 0), QImage()}, pMain->size());
+}
+
+void CalendarEx::SetCalendarSync(const QDate &defaultDate)
+{
+    if (nullptr == m_pCalendar || !defaultDate.isValid()) return;
+
+    m_pCalendar->setCurrentPage(defaultDate.year(), defaultDate.month());
+    m_pCalendar->setSelectedDate(defaultDate);
+}
+
+ScopedCalendar::ScopedCalendar(const QDate &from, const QDate &to, QWidget *parent) :
+    CalendarInterface(parent),
+    m_pCalendar_L(nullptr),
+    m_pCalendar_R(nullptr)
+{
+    QHBoxLayout* pLay = new QHBoxLayout(this);
+    QWidget* pMain = new QWidget(this);
+    pMain->setLayout(m_pLayout);
+
+    pLay->addWidget(pMain);
+    pLay->setMargin(SHADOW_RADIUS);
+
+    ScopeSelectionModel *pDateScopeModel = new ScopeSelectionModel();
+    if(from.isValid() && to.isValid())
+    {
+        pDateScopeModel->dtFirst = from;
+        pDateScopeModel->dtSecond = to;
+        pDateScopeModel->bLocked = true;
+    }
+    m_pCalendar_L = new CalendarWidgetEx(this);
+    m_pCalendar_L->setStyleSheet("QCalendarView{background-color: transparent;}");
+    m_pCalendar_L->SetSelectMode(CalendarWidgetEx::Scope, pDateScopeModel);
+    m_pCalendar_L->hideNavigatioinButton(false, false, true, true);
+    m_pLayout->addWidget(m_pCalendar_L);
+
+    m_pCalendar_R = new CalendarWidgetEx(this);
+    m_pCalendar_R->setStyleSheet("QCalendarView{background-color: transparent;}");
+    m_pCalendar_R->SetSelectMode(CalendarWidgetEx::Scope, pDateScopeModel);
+    m_pCalendar_R->hideNavigatioinButton(true, true, false, false);
+    m_pLayout->addWidget(m_pCalendar_R);
+
+    m_pLayout->setSpacing(0);
+    m_pLayout->setMargin(0);
+    pMain->resize(2 * CALENDAR_WIDTH, CALENDAR_HEIGHT);
+    SetCalendarSync(from);
+
+    connect(pDateScopeModel, &ScopeSelectionModel::sig_ScopeSelected, this, &ScopedCalendar::OnScopeSelected);
+
+    resize(2 * CALENDAR_WIDTH + 2 * SHADOW_RADIUS, CALENDAR_HEIGHT + 2 * SHADOW_RADIUS);
+    SetDropShadow(BoxShadow{0, 0, SHADOW_RADIUS, 0, QColor(0, 0, 0, 60), QSize(0, 0), QImage()}, pMain->size());
+}
+
+void ScopedCalendar::SetMinimumDate(const QDate &date)
+{
+    if (nullptr == m_pCalendar_L || nullptr == m_pCalendar_R) return;
+
+    m_pCalendar_L->setMinimumDate(date);
+    m_pCalendar_R->setMinimumDate(date);
+}
+
+void ScopedCalendar::SetMaximumDate(const QDate &date)
+{
+    if (nullptr == m_pCalendar_L || nullptr == m_pCalendar_R) return;
+
+    m_pCalendar_L->setMaximumDate(date);
+    m_pCalendar_R->setMaximumDate(date);
+}
+
+void ScopedCalendar::OnScopeSelected(const QDate &from, const QDate &to)
+{
+    emit sig_ScopeSelected(from, to);
+    close();
+}
+
+void ScopedCalendar::SetCalendarSync(const QDate &defaultDate)
+{
+    if (nullptr == m_pCalendar_L || nullptr == m_pCalendar_R || !defaultDate.isValid()) return;
+
+    m_pCalendar_L->setCurrentPage(defaultDate.year(), defaultDate.month());
+    connect(m_pCalendar_L, &QCalendarWidget::currentPageChanged, this, [&](int year, int month) {
+        QDate nextMonth = QDate(year, month, 1).addMonths(1);
+        m_pCalendar_R->setCurrentPage(nextMonth.year(), nextMonth.month());
+    });
+
+    m_pCalendar_R->setCurrentPage(defaultDate.addMonths(1).year(), defaultDate.addMonths(1).month());
+    connect(m_pCalendar_R, &QCalendarWidget::currentPageChanged, this, [&](int year, int month) {
+        QDate prevMonth = QDate(year, month, 1).addMonths(-1);
+        m_pCalendar_L->setCurrentPage(prevMonth.year(), prevMonth.month());
+    });
+}

+ 66 - 0
common/DateSelect/calendarex.h

@@ -0,0 +1,66 @@
+#ifndef CALENDAREX_H
+#define CALENDAREX_H
+
+#include <QWidget>
+#include "calendarwidgetex.h"
+#include "DropShadow/idropshadowable.h"
+
+class QHBoxLayout;
+
+class CalendarInterface : public QWidget, public IDropShadowable
+{
+    Q_OBJECT
+public:
+    explicit CalendarInterface(QWidget *parent = nullptr);
+    virtual ~CalendarInterface();
+    /* 弹出位置? */
+    void positionCalendarPopup(QWidget *q);
+protected:
+     virtual void SetCalendarSync(const QDate &defaultDate) = 0;
+protected:
+     QHBoxLayout* m_pLayout;
+
+     const int SHADOW_RADIUS = 16;
+     const int SPACING = 8;
+     const int CALENDAR_WIDTH = 325;
+     const int CALENDAR_HEIGHT = 350;
+};
+/**
+ * @brief 带有阴影的日历
+ *
+ */
+class CalendarEx : public CalendarInterface
+{
+    Q_OBJECT
+public:
+    explicit CalendarEx(const QDate& defaultDate = QDate(), QWidget* parent = nullptr);
+    virtual ~CalendarEx() {}
+signals:
+    void sig_DateChanged(const QDate& date);
+private:
+    virtual void SetCalendarSync(const QDate &defaultDate);
+private:
+     CalendarWidgetEx *m_pCalendar;
+};
+
+class ScopedCalendar : public CalendarInterface
+{
+    Q_OBJECT
+public:
+    explicit ScopedCalendar(const QDate &from = QDate(), const QDate &to = QDate(), QWidget *parent = nullptr);
+    virtual ~ScopedCalendar() { };
+
+    void SetMinimumDate(const QDate &date);
+    void SetMaximumDate(const QDate &date);
+signals:
+    void sig_ScopeSelected(const QDate &from, const QDate &to);
+public slots:
+    void OnScopeSelected(const QDate &from, const QDate &to);
+private:
+    virtual void SetCalendarSync(const QDate &defaultDate);
+private:
+     CalendarWidgetEx *m_pCalendar_L;
+     CalendarWidgetEx *m_pCalendar_R;
+};
+
+#endif // CALENDAREX_H

+ 67 - 0
common/DateSelect/calendarheader.cpp

@@ -0,0 +1,67 @@
+#include "calendarheader.h"
+#include "ui_calendarheader.h"
+#include "PaintHelper/painthelper.h"
+// #include "StyleManager/lhstylemanager.h"
+#include "LHQLogAPI.h"
+
+#include <QFile>
+
+CalendarHeader::CalendarHeader(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::CalendarHeader)
+{
+    ui->setupUi(this);
+    // LHStyleManager::Instance()->AddWidget(this);
+    // setQSS();
+    setQSS(0);
+}
+
+CalendarHeader::~CalendarHeader()
+{
+    delete ui;
+}
+
+void CalendarHeader::SetFirstDayOfWeek(Qt::DayOfWeek dayOfWeek)
+{
+    ui->label1->setText(km_listWeeks.at((dayOfWeek-1+0)%km_listWeeks.count()));
+    ui->label2->setText(km_listWeeks.at((dayOfWeek-1+1)%km_listWeeks.count()));
+    
+    ui->label3->setText(km_listWeeks.at((dayOfWeek-1+2)%km_listWeeks.count()));
+    ui->label4->setText(km_listWeeks.at((dayOfWeek-1+3)%km_listWeeks.count()));
+    
+    ui->label5->setText(km_listWeeks.at((dayOfWeek-1+4)%km_listWeeks.count()));
+    ui->label6->setText(km_listWeeks.at((dayOfWeek-1+5)%km_listWeeks.count()));
+    
+    ui->label7->setText(km_listWeeks.at((dayOfWeek-1+6)%km_listWeeks.count()));
+}
+
+/* 设置QSS */
+void CalendarHeader::setQSS(int style)
+{
+    QString qssPath;
+    if(style == 0)
+    {
+        /* 亮色UI */
+        qssPath = ":/Calendar/qss/calendarheader_light.qss";
+    }
+    else if(style == 1)
+    {
+        /* 暗色UI */
+        qssPath = ":/Calendar/qss/calendarheader_dark.qss";
+    }
+    QFile qssFile(qssPath);
+    if(qssFile.open(QFile::ReadOnly))
+    {
+        QString qss = qssFile.readAll();
+        setStyleSheet(qss);
+        qssFile.close();
+    }else {
+        LH_WRITE_ERROR("open qss file failed, path: " + qssPath);
+    }
+}
+
+
+void CalendarHeader::paintEvent(QPaintEvent *)
+{
+    PainterEx painter(this);
+}

+ 32 - 0
common/DateSelect/calendarheader.h

@@ -0,0 +1,32 @@
+#ifndef CALENDARHEADER_H
+#define CALENDARHEADER_H
+
+#include <QWidget>
+
+namespace Ui {
+class CalendarHeader;
+}
+
+class CalendarHeader : public QWidget
+{
+    Q_OBJECT
+    
+public:
+    explicit CalendarHeader(QWidget *parent = nullptr);
+    ~CalendarHeader() override;
+    void SetFirstDayOfWeek(Qt::DayOfWeek dayOfWeek);
+    
+    /* 设置QSS */
+    void setQSS(int style);
+
+public slots:
+
+    
+private:
+    Ui::CalendarHeader *ui;
+    const QList<QString> km_listWeeks = {"一","二","三","四","五","六","日"};
+protected:
+    void paintEvent(QPaintEvent *) override;
+};
+
+#endif // CALENDARHEADER_H

+ 139 - 0
common/DateSelect/calendarheader.ui

@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CalendarHeader</class>
+ <widget class="QWidget" name="CalendarHeader">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>361</width>
+    <height>19</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>0</width>
+    <height>16</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>16777215</width>
+    <height>19</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <property name="spacing">
+    <number>0</number>
+   </property>
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QLabel" name="label1">
+     <property name="styleSheet">
+      <string notr="true"/>
+     </property>
+     <property name="text">
+      <string>日</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label2">
+     <property name="styleSheet">
+      <string notr="true"/>
+     </property>
+     <property name="text">
+      <string>一</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label3">
+     <property name="styleSheet">
+      <string notr="true"/>
+     </property>
+     <property name="text">
+      <string>二</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label4">
+     <property name="styleSheet">
+      <string notr="true"/>
+     </property>
+     <property name="text">
+      <string>三</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label5">
+     <property name="styleSheet">
+      <string notr="true"/>
+     </property>
+     <property name="text">
+      <string>四</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label6">
+     <property name="styleSheet">
+      <string notr="true"/>
+     </property>
+     <property name="text">
+      <string>五</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label7">
+     <property name="styleSheet">
+      <string notr="true"/>
+     </property>
+     <property name="text">
+      <string>六</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 94 - 0
common/DateSelect/calendarnav.cpp

@@ -0,0 +1,94 @@
+#include "calendarnav.h"
+#include "PaintHelper/painthelper.h"
+#include "ui_calendarnav.h"
+// #include "StyleManager/lhstylemanager.h"
+#include <QDebug>
+#include <QFile>
+
+
+//#pragma execution_character_set("utf-8")
+CalendarNav::CalendarNav(QCalendarWidget *pCalendar, QWidget *parent)
+   : QWidget(parent)
+   , ui(new Ui::CalendarNav)
+   , m_pCalendar(pCalendar)
+{
+    ui->setupUi(this);
+
+    /* 加载qss */
+    // QFile file = QString(":/Calendar/qss/CalendarNav_light.qss");
+    // if(!file.open(QIODevice::ReadOnly))
+    // {
+    //     qDebug() << "CalendarNav.cpp:QSS打开失败";
+    // }
+    // QString  ss = file.readAll();
+
+    // this->setStyleSheet(ss);
+
+    connect(ui->btnPrevYear, &QPushButton::clicked, m_pCalendar, &QCalendarWidget::showPreviousYear);
+    connect(ui->btnPrevMonth, &QPushButton::clicked, m_pCalendar, &QCalendarWidget::showPreviousMonth);
+    connect(ui->btnNextMonth, &QPushButton::clicked, m_pCalendar, &QCalendarWidget::showNextMonth);
+    connect(ui->btnNextYear, &QPushButton::clicked, m_pCalendar, &QCalendarWidget::showNextYear);
+    
+    connect(m_pCalendar, &QCalendarWidget::currentPageChanged, this, &CalendarNav::SetYearMonth);
+    
+    //默认显示当天, QCalendarWidget的默认选中日期也是当天
+    SetYearMonth(QDate::currentDate().year(), QDate::currentDate().month());
+    // LHStyleManager::Instance()->AddWidget(this);
+    setQSS();
+}
+
+CalendarNav::~CalendarNav()
+{
+    delete ui;
+}
+
+void CalendarNav::hidePreYear(bool flag)
+{
+    ui->btnPrevYear->setVisible(!flag);
+}
+
+void CalendarNav::hidePreMonth(bool flag)
+{
+    ui->btnPrevMonth->setVisible(!flag);
+}
+
+void CalendarNav::hideNextYear(bool flag)
+{
+    ui->btnNextYear->setVisible(!flag);
+}
+
+void CalendarNav::hideNextMonth(bool flag)
+{
+    ui->btnNextMonth->setVisible(!flag);
+}
+
+/* 设置QSS */
+void CalendarNav::setQSS(int style)
+{
+    QString qssPath = QApplication::applicationDirPath();
+    QString qssFile;
+    if(style == 0)
+    {
+        // qssFile = qssPath + "/white/SelectDate/calendarnav.qss";
+        qssFile = ":/Calendar/qss/CalendarNav_light.qss";
+    }else if(style == 1)
+    {
+        // qssFile = qssPath + "/black/SelectDate/calendarnav.qss";
+        qssFile = ":/Calendar/qss/CalendarNav_dark.qss";
+    }
+    QFile file(qssFile);
+    if(file.open(QIODevice::ReadOnly))
+    {
+        QString stylesheet = file.readAll();
+        this->setStyleSheet(stylesheet);
+        file.close();
+    } else
+    {
+        qDebug() << "打开文件失败:" << file.fileName();
+    }
+}
+
+void CalendarNav::SetYearMonth(int year, int month)
+{
+    ui->labelYearMonth->setText(QString("%1 年 %2 月").arg(year).arg(month));
+}

+ 33 - 0
common/DateSelect/calendarnav.h

@@ -0,0 +1,33 @@
+#ifndef CALENDARNAV_H
+#define CALENDARNAV_H
+
+#include <QWidget>
+#include <QCalendarWidget>
+
+namespace Ui {
+class CalendarNav;
+}
+
+class CalendarNav : public QWidget
+{
+    Q_OBJECT
+    
+public:
+    explicit CalendarNav(QCalendarWidget *pCalendar, QWidget *parent = nullptr);
+    ~CalendarNav() override;
+
+    void hidePreYear(bool);
+    void hidePreMonth(bool);
+    void hideNextYear(bool);
+    void hideNextMonth(bool);
+
+    /* 设置QSS */
+    void setQSS(int style = 0);
+private:
+    Ui::CalendarNav *ui;
+    QCalendarWidget *m_pCalendar;
+private slots:
+    void SetYearMonth(int year, int month);
+};
+
+#endif // CALENDARNAV_H

+ 140 - 0
common/DateSelect/calendarnav.ui

@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CalendarNav</class>
+ <widget class="QWidget" name="CalendarNav">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>361</width>
+    <height>39</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>0</width>
+    <height>39</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>16777215</width>
+    <height>39</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <property name="spacing">
+    <number>15</number>
+   </property>
+   <property name="leftMargin">
+    <number>6</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>6</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QPushButton" name="btnPrevYear">
+     <property name="minimumSize">
+      <size>
+       <width>12</width>
+       <height>12</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>12</width>
+       <height>12</height>
+      </size>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QPushButton" name="btnPrevMonth">
+     <property name="minimumSize">
+      <size>
+       <width>12</width>
+       <height>12</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>12</width>
+       <height>12</height>
+      </size>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="labelYearMonth">
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>2020 年 8 月</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QPushButton" name="btnNextMonth">
+     <property name="minimumSize">
+      <size>
+       <width>12</width>
+       <height>12</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>12</width>
+       <height>12</height>
+      </size>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QPushButton" name="btnNextYear">
+     <property name="minimumSize">
+      <size>
+       <width>12</width>
+       <height>12</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>12</width>
+       <height>12</height>
+      </size>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 408 - 0
common/DateSelect/calendarwidgetex.cpp

@@ -0,0 +1,408 @@
+//qcustomcalendarwidget.cpp
+#include "calendarwidgetex.h"
+#include <QPainter>
+#include <QProxyStyle>
+#include <QTableView>
+#include <QHeaderView>
+#include <QLayout>
+#include <QPushButton>
+#include <QLabel>
+#include <QDebug>
+#include <QLayoutItem>
+#include <QKeyEvent>
+#include <QPainterPath>
+
+#include "scopeselectionmodel.h"
+#include "PaintHelper/painthelper.h"
+// #include "StyleManager/lhstylemanager.h"
+//#include "utility/utility.h"
+#include "UIStyleManager.h"
+
+CalendarWidgetEx::CalendarWidgetEx(QWidget *parent)
+    : QCalendarWidget(parent)
+    , m_modeSelection(Normal)
+    , m_pDateScopeModel(nullptr)
+    , m_nLineHeight(-1)
+    , m_hasTopSplitLine(false)
+{
+    setWindowFlag(Qt::NoDropShadowWindowHint);
+    setAttribute(Qt::WA_TranslucentBackground);
+    setWindowFlag(Qt::FramelessWindowHint);
+
+    //使用固定尺寸(无法通过resize控制日历大小, 日历整体大小由layout下的控件的fixSize决定)
+    //layout()->setSizeConstraint(QLayout::SetFixedSize);
+    //禁用原有的年月导航
+    setNavigationBarVisible(false);
+    //禁用横向纵向表头
+    setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
+    setHorizontalHeaderFormat(QCalendarWidget::NoHorizontalHeader);
+    //取消聚焦虚线框
+    setStyle(new NoFocusStyle(this));
+
+    QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
+    if(vBodyLayout == nullptr) return;
+
+    m_pNav = new CalendarNav(this);
+    m_pHeader = new CalendarHeader(this);
+    setFirstDayOfWeek(Qt::Sunday);
+    m_pHeader->SetFirstDayOfWeek(Qt::Sunday);
+
+    vBodyLayout->insertWidget(0, m_pNav);
+    // 导航和星期标题间距
+    vBodyLayout->insertSpacing(1, 10);
+    vBodyLayout->insertWidget(2, m_pHeader);
+
+    vBodyLayout->setSpacing(10);
+    vBodyLayout->setContentsMargins(10,0,10,10);
+
+    m_nLineHeight = vBodyLayout->itemAt(4)->widget()->mapTo(this, QPoint(0,0)).y();
+    //qDebug()<<"m_nLineHeight"<<m_nLineHeight<<vBodyLayout->itemAt(3)->widget()->height();
+
+    //开启鼠标监测
+    QTableView *pCalendarView = dynamic_cast<QTableView*>(vBodyLayout->itemAt(4)->widget());
+    if (Q_NULLPTR != pCalendarView) {
+        pCalendarView->setMouseTracking(true);
+    }
+
+    //在构造函数里取消selectionChanged, clicked事件没用, 因为执行QDateTimeEdit的setCalendarWidget方法时, 会重新绑定
+    //所以必须等setCalendarWidget执行完后再取消事件
+    //calendarWidget->disconnect(calendarWidget, &QCalendarWidget::selectionChanged, 0, 0);
+    //calendarWidget->disconnect(calendarWidget, &QCalendarWidget::clicked, 0, 0);
+
+    setMouseTracking(true);
+
+    // 设置默认字体后,修复单元格变得很宽的问题
+    for (QWidget* f : findChildren<QWidget*>()) {
+        if(f->objectName() != "qt_calendar_calendarview") continue;
+        QTableView* pView = reinterpret_cast<QTableView*>(f);
+        if (nullptr != pView && nullptr != pView->horizontalHeader()) {
+            pView->horizontalHeader()->setMaximumSectionSize(WINDOW_WIDTH / 8);
+        }
+    }
+
+    connect(this, &QCalendarWidget::clicked, this, &CalendarWidgetEx::OnClicked);
+    // initSkinColor();
+    setUIStyle();
+
+    /* 设置对象名称,加载QSS */
+    // this->setObjectName("calendarWidget");
+    this->setStyleSheet("background-color:transparent;");
+}
+
+
+void CalendarWidgetEx::SetSelectMode(SelectMode mode, ScopeSelectionModel *pDataModel)
+{
+    m_listMultiSelectDays.clear();
+    m_modeSelection = mode;
+    if(m_pDateScopeModel == nullptr)
+    {
+        m_pDateScopeModel = pDataModel;
+        connect(m_pDateScopeModel, &ScopeSelectionModel::sig_Update, this, static_cast<void(QWidget::*)()>(&QWidget::update));
+    }
+    update();
+}
+
+void CalendarWidgetEx::hideNavigatioinButton(bool bPreYear, bool bPreMon, bool bNextYear, bool bNextMon)
+{
+    QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
+    if(vBodyLayout == nullptr) return;
+    CalendarNav *pNav = qobject_cast<CalendarNav*>(vBodyLayout->itemAt(0)->widget());
+    if (nullptr != pNav) {
+        pNav->hideNextMonth(bNextMon);
+        pNav->hideNextYear(bNextYear);
+        pNav->hidePreMonth(bPreMon);
+        pNav->hidePreYear(bPreYear);
+    }
+}
+
+/* 设置UI样式,0亮色,1暗色 */
+void CalendarWidgetEx::setUIStyle()
+{
+    if(EPUIStyle.getUIStyle() == enum_UIStyle::UI_Light)
+    {
+        m_style = 0;
+    }
+    else if(EPUIStyle.getUIStyle() == enum_UIStyle::UI_Dark)
+    {
+        m_style = 1;
+    }
+    initSkinColor();
+    m_pNav->setQSS(m_style);
+    m_pHeader->setQSS(m_style);
+    update();
+}
+
+void CalendarWidgetEx::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
+{
+    PainterEx *painterEx = static_cast<PainterEx*>(painter);
+    painterEx->setRenderHint(QPainter::Antialiasing);
+
+    QColor textColor;
+    {//正常
+        textColor = m_normalTextColor;
+    }
+#if 0
+    // 周六,周日特殊颜色
+    int nWeek = date.dayOfWeek();
+    if (6 == nWeek || 7 == nWeek) {
+        textColor = QColor(255,149,0);
+    }
+#endif
+    //鼠标移入
+    if(date == m_dateMouseOver)
+    {
+        QPoint center = rect.center();
+        QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
+        painterEx->DrawRoundedRect(rc, 2.0, m_hoverBlockColor);
+        textColor = m_normalTextColor;
+    }
+    //当天
+    if(date == QDate::currentDate())
+    {
+        painter->save();
+        QPoint center = rect.center();
+        QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
+        painter->setPen(m_todayTextColor);
+        painter->setBrush(Qt::transparent);
+        painter->drawRoundedRect(rc, 2.0, 2.0);
+        textColor = m_todayTextColor;
+        painter->restore();
+    }
+    //选中
+    if(m_modeSelection == Multi && m_listMultiSelectDays.contains(date))
+    {
+        //painterEx->DrawCircle(QRectF(rect).center(), 9, QColor(9,109,217));
+        QPoint center = rect.center();
+        QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
+        painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
+        textColor = m_selectTextColor;
+    }
+    if(m_modeSelection == Normal && date == selectedDate())
+    {
+        //painterEx->DrawCircle(QRectF(rect).center(), 9, QColor(9, 109, 217));
+        QPoint center = rect.center();
+        QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
+        painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
+        textColor = m_selectTextColor;
+    }
+    if(m_modeSelection == Scope && m_pDateScopeModel != nullptr && m_pDateScopeModel->dtFirst.isValid() && m_pDateScopeModel->dtSecond.isValid())
+    {
+        QDate scopeStart = qMin(m_pDateScopeModel->dtFirst, m_pDateScopeModel->dtSecond);
+        QDate scopeEnd = qMax(m_pDateScopeModel->dtFirst, m_pDateScopeModel->dtSecond);
+        if(date == qBound(scopeStart, date, scopeEnd) && date.month() == monthShown())
+        {
+            painterEx->SetBrushOnly(m_hoverBlockColor);
+            if(date == scopeStart || date == scopeEnd)
+            {
+                textColor = m_selectTextColor;
+                QPoint center = rect.center();
+                QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
+                painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
+            }
+            else
+            {
+                QRect r(0, 0, rect.width(), TEXT_WIDTH);
+                r.moveCenter(rect.center());
+                painterEx->drawRect(r);
+            }
+        }
+    }
+    //不可选的日期或非当月日期
+    if(date < minimumDate() || date > maximumDate() || date.month() != monthShown())
+    {
+        textColor = m_disableTextColor;
+    }
+
+    //当天且未选中, 加粗
+    bool isBold = (date == QDate::currentDate() && date != selectedDate());
+    QString strDay(QString("%1").arg(date.day(), 2, 10, QLatin1Char('0')));
+    painterEx->setFont(FontEx(font().family(), DEFAULT_FONT_SIZE, isBold));
+    QRect rc = rect.adjusted(-1, 0, 0, -3); // 矫正文字位置
+    painterEx->DrawText(rc, strDay, textColor, Qt::AlignCenter);
+}
+
+void CalendarWidgetEx::paintEvent(QPaintEvent *)
+{
+    PainterEx painter(this);
+
+    //边框和背景
+    painter.setPen(Qt::transparent);
+    painter.setBrush(m_bgBrushColor);
+    QRect rc(rect());
+    painter.DrawRoundedRect(rc, WINDOW_RADIUS);
+
+    QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
+    if(vBodyLayout != nullptr) {
+        // 画导航栏,绘制上圆角
+        QPainterPath path;
+        path.setFillRule(Qt::WindingFill);
+        QRectF tmpRc(1, 0, WINDOW_WIDTH - WINDOW_RADIUS, 40);
+        path.addRoundedRect(tmpRc, WINDOW_RADIUS, WINDOW_RADIUS);
+        path.addRect(QRectF(tmpRc.x(), tmpRc.y() + WINDOW_RADIUS, tmpRc.width(), tmpRc.height()));
+        // painter.fillPath(path, QColor(255, 255, 255));
+        painter.fillPath(path, m_bgBrushColor);
+        //分割线
+        QWidget* pNav = vBodyLayout->itemAt(0)->widget();
+        if (nullptr != pNav) {
+            int h = pNav->mapTo(this, pNav->rect().bottomRight()).y() + 2;
+            // painter.SetPenOnly(QColor(0, 0, 0, 23));
+            painter.setPen(m_bgPenColor);
+            painter.drawLine(QPoint(0, h), QPoint(width(), h));
+        }
+    }
+}
+
+QSize CalendarWidgetEx::minimumSizeHint() const
+{
+    return QSize(WINDOW_WIDTH, WINDOW_HEIGHT);
+}
+
+void CalendarWidgetEx::mouseMoveEvent(QMouseEvent *event)
+{
+    QCalendarWidget::mouseMoveEvent(event);
+
+    QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
+    if(vBodyLayout == nullptr) return;
+    QTableView *pCalendarView = dynamic_cast<QTableView*>(vBodyLayout->itemAt(4)->widget());
+    if(pCalendarView == nullptr) return;
+    QModelIndex index = pCalendarView->indexAt(pCalendarView->mapFromGlobal(event->globalPos()));
+    QDate dateMouseOver = dateForCell(index.row(), index.column());
+    if(m_dateMouseOver != dateMouseOver)
+    {
+        m_dateMouseOver = dateMouseOver;
+        if(m_pDateScopeModel != nullptr && !m_pDateScopeModel->bLocked && m_pDateScopeModel->dtFirst.isValid())
+        {
+            m_pDateScopeModel->dtSecond = dateMouseOver;
+            m_pDateScopeModel->Update();
+        }
+        update();
+    }
+}
+
+//仅适用于: 不显示纵向表头(第几周), 且不显示横向表头(周几)
+QDate CalendarWidgetEx::dateForCell(int row, int column) const
+{
+    if (row < 0 || row > 5 || column < 0 || column > 6)
+        return QDate();
+
+    const QDate refDate = referenceDate();
+    if (!refDate.isValid())
+        return QDate();
+
+    const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate);
+    if (columnForFirstOfShownMonth - 0/*m_firstColumn*/ < 1)
+        row -= 1;
+
+    const int requestedDay = 7 * (row - 0/*m_firstRow*/) + column - columnForFirstOfShownMonth - refDate.day() + 1;
+    return refDate.addDays(requestedDay);
+}
+
+QDate CalendarWidgetEx::referenceDate() const
+{
+    int refDay = 1;
+    while (refDay <= 31) {
+        QDate refDate(yearShown(), monthShown(), refDay);
+        if (refDate.isValid())
+            return refDate;
+        refDay += 1;
+    }
+    return QDate();
+}
+int CalendarWidgetEx::columnForFirstOfMonth(const QDate &date) const
+{
+    return (columnForDayOfWeek(date.dayOfWeek()) - (date.day() % 7) + 8) % 7;
+}
+int CalendarWidgetEx::columnForDayOfWeek(int day) const
+{
+    if (day < 1 || day > 7)
+        return -1;
+    int column = day - firstDayOfWeek();
+    if (column < 0)
+        column += 7;
+    return column;
+}
+
+void CalendarWidgetEx::initSkinColor()
+{
+    switch (m_style) 
+    {
+    case 0:
+        m_normalTextColor = NORMAL_TEXT_BRIGHT;
+        m_todayTextColor = TODAY_TEXT_BRIGHT;
+        m_selectTextColor = SELECT_TEXT_BRIGHT;
+        m_disableTextColor = DISABLE_TEXT_BRIGHT;
+        m_splitLineColor = SPLIT_LINE_BRIGHT;
+        m_selectBlockColor = SELECT_BRIGHT;
+        m_hoverBlockColor = HOVER_BRIGHT;
+        m_bgBrushColor = BG_BRUSH_BRIGHT;
+        m_bgPenColor = BG_PEN_BRIGHT;
+        break;
+    case 1:
+        m_normalTextColor = NORMAL_TEXT_DEEP;
+        m_todayTextColor = TODAY_TEXT_DEEP;
+        m_selectTextColor = SELECT_TEXT_DEEP;
+        m_disableTextColor = DISABLE_TEXT_DEEP;
+        m_splitLineColor = SPLIT_LINE_DEEP;
+        m_selectBlockColor = SELECT_DEEP;
+        m_hoverBlockColor = HOVER_DEEP;
+        m_bgBrushColor = BG_BRUSH_DEEP;
+        m_bgPenColor = BG_PEN_DEEP;
+        break;
+    default:
+        break;
+    }
+}
+
+void CalendarWidgetEx::OnClicked(const QDate &date)
+{
+    if(m_modeSelection == Multi)
+    {
+        if(m_listMultiSelectDays.contains(date))
+        {
+            m_listMultiSelectDays.removeOne(date);
+        }
+        else
+        {
+            m_listMultiSelectDays.append(date);
+        }
+    }
+    if(m_modeSelection == Scope && m_pDateScopeModel != nullptr)
+    {
+        if(!m_pDateScopeModel->bLocked && m_pDateScopeModel->dtFirst.isValid())
+        {
+            m_pDateScopeModel->dtSecond = date;
+            m_pDateScopeModel->bLocked = true;
+            m_pDateScopeModel->Locked();
+        }
+        else
+        {
+            m_pDateScopeModel->dtFirst = date;
+            m_pDateScopeModel->dtSecond = date;
+            m_pDateScopeModel->bLocked = false;
+        }
+
+        m_pDateScopeModel->Update();
+    }
+    update();
+}
+
+void CalendarWidgetEx::leaveEvent(QEvent *)
+{
+    if(m_modeSelection != Scope)
+    {
+        //离开日历时, 清空鼠标移入状态
+        m_dateMouseOver = QDate();
+        update();
+    }
+}
+
+void NoFocusStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
+{
+    QStyleOption *viewOption = new QStyleOption(*option);
+    viewOption->state &= (~QStyle::State_HasFocus);
+    //if (element == PE_FrameFocusRect) return;
+    QProxyStyle::drawPrimitive(element, viewOption, painter, widget);
+    delete viewOption;
+}
+
+

+ 117 - 0
common/DateSelect/calendarwidgetex.h

@@ -0,0 +1,117 @@
+#ifndef QCUSTOMCALENDARWIDGET_H
+#define QCUSTOMCALENDARWIDGET_H
+
+#include <QCalendarWidget>
+#include <QPushButton>
+#include <QLabel>
+#include <QProxyStyle>
+
+#include "calendarheader.h"
+#include "calendarnav.h"
+
+class ScopeSelectionModel;
+
+class NoFocusStyle : public QProxyStyle
+{
+    Q_OBJECT
+public:
+    explicit NoFocusStyle(QObject *parent):QProxyStyle() { setParent(parent); }
+
+private:
+    void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+                       QPainter *painter, const QWidget *widget) const;
+};
+/**
+ * @brief The CalendarWidgetEx class
+ */
+class CalendarWidgetEx : public QCalendarWidget
+{
+    Q_OBJECT
+public:
+    enum SelectMode
+    {
+        Normal,
+        Multi,
+        Scope,
+    };
+public:
+    explicit CalendarWidgetEx(QWidget *parent = nullptr);
+    ~CalendarWidgetEx() override {}
+    void SetSelectMode(SelectMode mode, ScopeSelectionModel *pDataModel = nullptr);
+    QList<QDate> GetSelectDays(){return m_listMultiSelectDays;}
+
+    void hideNavigatioinButton(bool bPreYear, bool bPreMon, bool bNextYear, bool bNextMon);
+
+    /* 设置UI样式,0亮色,1暗色 */
+    void setUIStyle();
+
+private:
+    QDate dateForCell(int row, int column) const;
+    QDate referenceDate() const;
+    int columnForFirstOfMonth(const QDate &date) const;
+    int columnForDayOfWeek(int day) const;
+    void initSkinColor();
+protected:
+    void paintCell(QPainter *painter, const QRect &rect, const QDate &date) const override;
+    void paintEvent(QPaintEvent *) override;
+    void mouseMoveEvent(QMouseEvent *event) override;
+    void leaveEvent(QEvent *) override;
+public:
+    QSize minimumSizeHint() const override;
+private slots:
+    void OnClicked(const QDate &date);
+private:
+    int m_style = 0;
+    CalendarNav* m_pNav = nullptr;
+    CalendarHeader* m_pHeader = nullptr;
+    /* 亮色 */
+    const QColor NORMAL_TEXT_BRIGHT{QColor(58,63,99)};
+    const QColor TODAY_TEXT_BRIGHT{QColor(68,88,254)};
+    const QColor SELECT_TEXT_BRIGHT{Qt::white};
+    const QColor DISABLE_TEXT_BRIGHT{QColor(133, 142, 189)};
+    const QColor SPLIT_LINE_BRIGHT{QColor(0, 0, 0, 23)};
+    const QColor SELECT_BRIGHT{QColor(68, 88, 254)};
+    const QColor HOVER_BRIGHT{QColor(227, 238, 255)};
+    const QColor BG_BRUSH_BRIGHT{QColor(255,255,255)};      /* 背景 */
+    const QColor BG_PEN_BRIGHT{QColor(0, 0, 0, 23)};        /* 线条 */
+
+    /* 暗色 */
+    const QColor NORMAL_TEXT_DEEP{QColor(255,255,255,230)};
+    const QColor TODAY_TEXT_DEEP{Qt::black};
+    const QColor SELECT_TEXT_DEEP{Qt::black};
+    const QColor DISABLE_TEXT_DEEP{QColor(255,255,255,77)};
+    const QColor SPLIT_LINE_DEEP{QColor(255,255,255,64)};
+    const QColor SELECT_DEEP{QColor(233, 161, 18)};
+    const QColor HOVER_DEEP{QColor(132, 134, 136)};
+    
+    const QColor TODAY_DEEP{QColor(67, 142, 255)};
+    const QColor BG_PEN_DEEP{QColor(255,255,255,51)};       /* 线条 */
+    const QColor BG_BRUSH_DEEP{QColor{116,117,120}};        /* 背景 */
+    const QColor BG_SPECIAL_PEN_DEEP{QColor{27, 186, 102}}; 
+    const QColor BG_SPECIAL_BRUSH_DEEP{QColor{78, 112, 97}};
+
+
+
+    const int DEFAULT_FONT_SIZE = 16;
+    const int TEXT_WIDTH = 24;
+    const qreal WINDOW_RADIUS = 4.0;
+    const int WINDOW_WIDTH = 325;
+    const int WINDOW_HEIGHT = 350;
+
+    QDate m_dateMouseOver;
+    QList<QDate> m_listMultiSelectDays;
+    SelectMode m_modeSelection;
+    ScopeSelectionModel *m_pDateScopeModel;
+    int m_nLineHeight;      //星期和日之间的分割线的Y坐标
+    bool m_hasTopSplitLine; // 是否绘制顶部分割线
+    QColor m_normalTextColor;
+    QColor m_todayTextColor;
+    QColor m_selectTextColor;
+    QColor m_disableTextColor;
+    QColor m_splitLineColor;
+    QColor m_selectBlockColor;
+    QColor m_hoverBlockColor;
+    QColor m_bgBrushColor;          /* 背景画刷 */
+    QColor m_bgPenColor;            /* 背景画笔 */
+};
+#endif // QCUSTOMCALENDARWIDGET_H

+ 131 - 0
common/DateSelect/cdate.cpp

@@ -0,0 +1,131 @@
+#include "cdate.h"
+
+#include <QHBoxLayout>
+#include <QPainter>
+#include <QEvent>
+#include <QDebug>
+#include <QMouseEvent>
+#include <QEventLoop>
+
+#include "calendarwidgetex.h"
+#include "oneshadow.h"
+
+CDate::CDate(const QDate& defaultDate,QWidget *parent,PopupType type) :
+    QWidget{parent},
+    m_parent(parent),
+    m_type(type)
+{
+    init(defaultDate);
+}
+
+CDate::CDate(QWidget *parent,PopupType type) :
+    QWidget{parent},
+    m_parent(parent),
+    m_type(type)
+{
+//    if(type == Popup)
+//    {
+//        setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);
+//    }
+    init(QDate::currentDate());
+}
+
+/* 阻塞执行 */
+QDate CDate::execShow()
+{
+    this->show();
+    QEventLoop loop;
+    connect(this, &CDate::signal_close, &loop, &QEventLoop::quit);
+    loop.exec();
+    return m_date;
+}
+
+/* 设置日期 */
+void CDate::setDate(const QDate& date)
+{
+    m_date = date;
+    m_calendarEx->setSelectedDate(date);
+}
+
+
+void CDate::paintEvent(QPaintEvent *event)
+{
+    QPainter painter(this);
+    painter.setRenderHint(QPainter::Antialiasing);
+    painter.drawImage(QPoint(0,0),m_shadow->image());
+}
+/**
+ * @brief 在这里实现点击空白处隐藏或者关闭自身
+ * @param watched
+ * @param event
+ * @return
+ */
+bool CDate::eventFilter(QObject *watched, QEvent *event)
+{
+    auto me = dynamic_cast<QMouseEvent*>(event);
+    if(nullptr != me)
+    {
+        if(me->type() == QEvent::MouseButtonPress)
+        {
+            if(m_type == Popup)
+            {
+                /* 先将自身区域和鼠标坐标都转换成全局坐标 */
+                QPoint gTopLeft = this->mapToGlobal(this->rect().topLeft());
+                QRect gRect(gTopLeft.x(),gTopLeft.y(),this->width(),this->height());
+                /* 判断此时的鼠标坐标是否在这个控件中 */
+                if(!gRect.contains(me->globalPos()))
+                {
+//                    qDebug() << QTime::currentTime() << "CDate关闭了";
+                    emit signal_DateChanged(m_date);
+                    /* 发送信号,关闭自身 */
+                    emit signal_close();
+                    this->close();
+                }
+            }
+        }
+    }
+
+    return QWidget::eventFilter(watched,event);
+}
+
+
+void CDate::init(const QDate &defaultDate)
+{
+    /* 设置无边框 */
+    setWindowFlags(Qt::FramelessWindowHint);
+    /* 设置底层样式表,让最底层的透明 */
+    this->setAttribute(Qt::WA_TranslucentBackground);
+    /* 将父类的事件注册给自己 */
+    if(m_type == Popup && m_parent != nullptr)
+    {
+        m_parent->installEventFilter(this);
+    }
+    /* 设置布局,通过设置layout的Margin距离显示阴影 */
+    QHBoxLayout* layout = new QHBoxLayout(this);
+    this->setLayout(layout);
+    layout->setMargin(RADIUS);
+    this->resize(CALENDAR_WIDTH + RADIUS*2,CALENDAR_HEIGHT + RADIUS*2);
+
+    /* 设置日历组件 */
+    m_calendarEx = new CalendarWidgetEx(this);
+    m_calendarEx->resize(CALENDAR_WIDTH,CALENDAR_HEIGHT);
+    m_calendarEx->setCurrentPage(defaultDate.year(),defaultDate.month());
+    m_calendarEx->setSelectedDate(defaultDate);
+    layout->addWidget(m_calendarEx);
+
+
+    /* 设置自身时间 */
+    m_date = defaultDate;
+
+    /* 设置阴影 */
+    m_shadow = new OneShadow(QSize(m_calendarEx->width(),m_calendarEx->height()),RADIUS);
+
+
+    /* 信号和槽 */
+    connect(m_calendarEx,&CalendarWidgetEx::clicked,this,[this](const QDate& date){
+        m_date = date;
+        emit signal_DateChanged(date);
+        emit signal_close();
+        this->close();
+    });
+}

+ 63 - 0
common/DateSelect/cdate.h

@@ -0,0 +1,63 @@
+#ifndef CDATE_H
+#define CDATE_H
+
+
+/**
+ * 使用说明
+ *  1、这个是仿照TimeWidget做的一个可以点击空白处隐藏自身的日历控件
+ *  2、实现方式就是将父类的事件注册到这个控件上,拦截父类的鼠标点击,判断
+ *     是否在这个控件上进行隐藏,因此控件显示范围就依赖于父类空间大小了
+ *  3、使用方法:
+ *      1) 可以选择点击空白处隐藏或者不隐藏,设置PopuType类型就行
+ *
+*/
+
+#include <QDialog>
+#include <QWidget>
+#include <QDate>
+
+class CalendarWidgetEx;
+class OneShadow;
+
+class CDate : public QWidget
+{
+    Q_OBJECT
+
+    const int RADIUS = 16;              /* 阴影范围 */
+    const int CALENDAR_WIDTH = 325;     /* 日历大小 */
+    const int CALENDAR_HEIGHT = 350;
+public:
+    enum PopupType{
+        Popup = 0,
+        NoPopup = 1
+    };
+    CDate(const QDate& defaultDate,QWidget *parent = nullptr,PopupType type = Popup);
+    CDate(QWidget *parent = nullptr,PopupType type = Popup);
+
+    /* 阻塞执行 */
+    QDate execShow();
+    /* 设置日期 */
+    void setDate(const QDate& date);
+    /* 获取日期 */
+    QDate getDate() const { return m_date; }
+
+
+signals:
+    void signal_DateChanged(const QDate& date);
+    void signal_close();
+
+protected:
+    void paintEvent(QPaintEvent *event);
+    bool eventFilter(QObject *watched, QEvent *event);
+
+private:
+    void init(const QDate& defaultDate);
+private:
+    QDate m_date;
+    CalendarWidgetEx* m_calendarEx = nullptr;
+    OneShadow* m_shadow = nullptr;
+    QWidget* m_parent = nullptr;
+    PopupType m_type;
+};
+
+#endif // CDATE_H

BIN
common/DateSelect/icon/dark/nextMonth.png


BIN
common/DateSelect/icon/dark/nextMonth_hover.png


BIN
common/DateSelect/icon/dark/nextYear.png


BIN
common/DateSelect/icon/dark/nextYear_hover.png


BIN
common/DateSelect/icon/dark/preMonth.png


BIN
common/DateSelect/icon/dark/preMonth_hover.png


BIN
common/DateSelect/icon/dark/preYear.png


BIN
common/DateSelect/icon/dark/preYear_hover.png


BIN
common/DateSelect/icon/light/nextMonth.png


BIN
common/DateSelect/icon/light/nextMonth_hover.png


BIN
common/DateSelect/icon/light/nextYear.png


BIN
common/DateSelect/icon/light/nextYear_hover.png


BIN
common/DateSelect/icon/light/preMonth.png


BIN
common/DateSelect/icon/light/preMonth_hover.png


BIN
common/DateSelect/icon/light/preYear.png


BIN
common/DateSelect/icon/light/preYear_hover.png


+ 64 - 0
common/DateSelect/qss/CalendarNav_dark.qss

@@ -0,0 +1,64 @@
+
+
+QWidget
+{
+	color: rgba(255,255,255,0.9);
+	background-color: #747578;
+}
+
+
+QPushButton
+{
+	background: transparent;
+}
+
+QPushButton#btnPrevYear::hover
+{
+	image: url(:/Calendar/icon/dark/preYear.png);
+	border:none;
+}
+QPushButton#btnPrevYear
+{
+	image: url(:/Calendar/icon/dark/preYear_hover.png);
+	border:none;
+}
+
+QPushButton#btnPrevMonth::hover
+{
+	image: url(:/Calendar/icon/dark/preMonth.png);
+	border:none;
+}
+QPushButton#btnPrevMonth
+{
+	image: url(:/Calendar/icon/dark/preMonth_hover.png);
+	border:none;
+}
+
+QPushButton#btnNextMonth::hover
+{
+	image: url(:/Calendar/icon/dark/nextMonth.png);
+	border:none;
+}
+QPushButton#btnNextMonth
+{
+	image: url(:/Calendar/icon/dark/nextMonth_hover.png);
+	border:none;
+}
+
+QPushButton#btnNextYear::hover
+{
+	image: url(:/Calendar/icon/dark/nextYear.png);
+	border:none;
+}
+QPushButton#btnNextYear
+{
+	image: url(:/Calendar/icon/dark/nextYear_hover.png);
+	border:none;
+}
+
+QLabel 
+{
+	font-size: 14px;
+	font-weight: 500;
+	/* color: #3A3F63; */
+}

+ 50 - 0
common/DateSelect/qss/CalendarNav_light.qss

@@ -0,0 +1,50 @@
+QPushButton#btnPrevYear::hover
+{
+	image: url(:/Calendar/icon/light/preYear.png);
+	border:none;
+}
+QPushButton#btnPrevYear
+{
+	image: url(:/Calendar/icon/light/preYear_hover.png);
+	border:none;
+}
+
+QPushButton#btnPrevMonth::hover
+{
+	image: url(:/Calendar/icon/light/preMonth.png);
+	border:none;
+}
+QPushButton#btnPrevMonth
+{
+	image: url(:/Calendar/icon/light/preMonth_hover.png);
+	border:none;
+}
+
+QPushButton#btnNextMonth::hover
+{
+	image: url(:/Calendar/icon/light/nextMonth.png);
+	border:none;
+}
+QPushButton#btnNextMonth
+{
+	image: url(:/Calendar/icon/light/nextMonth_hover.png);
+	border:none;
+}
+
+QPushButton#btnNextYear::hover
+{
+	image: url(:/Calendar/icon/light/nextYear.png);
+	border:none;
+}
+QPushButton#btnNextYear
+{
+	image: url(:/Calendar/icon/light/nextYear_hover.png);
+	border:none;
+}
+
+QLabel 
+{
+	font-size: 14px;
+	font-weight: 500;
+	color: #3A3F63;
+}

+ 6 - 0
common/DateSelect/qss/calendarheader_dark.qss

@@ -0,0 +1,6 @@
+
+QWidget
+{
+    background: transparent;
+    color: rgba(255,255,255,0.9);
+}

+ 6 - 0
common/DateSelect/qss/calendarheader_light.qss

@@ -0,0 +1,6 @@
+
+QWidget
+{
+    background: transparent;
+    color: #3A3F63;
+}

+ 24 - 0
common/DateSelect/rescalendar.qrc

@@ -0,0 +1,24 @@
+<RCC>
+    <qresource prefix="/Calendar">
+        <file>icon/light/nextMonth.png</file>
+        <file>icon/light/nextMonth_hover.png</file>
+        <file>icon/light/nextYear.png</file>
+        <file>icon/light/nextYear_hover.png</file>
+        <file>icon/light/preMonth.png</file>
+        <file>icon/light/preMonth_hover.png</file>
+        <file>icon/light/preYear.png</file>
+        <file>icon/light/preYear_hover.png</file>
+        <file>icon/dark/nextMonth.png</file>
+        <file>icon/dark/nextMonth_hover.png</file>
+        <file>icon/dark/nextYear.png</file>
+        <file>icon/dark/nextYear_hover.png</file>
+        <file>icon/dark/preMonth.png</file>
+        <file>icon/dark/preMonth_hover.png</file>
+        <file>icon/dark/preYear.png</file>
+        <file>icon/dark/preYear_hover.png</file>
+        <file>qss/CalendarNav_light.qss</file>
+        <file>qss/CalendarNav_dark.qss</file>
+        <file>qss/calendarheader_light.qss</file>
+        <file>qss/calendarheader_dark.qss</file>
+    </qresource>
+</RCC>

+ 63 - 0
common/DateSelect/scopecalendarbtn.cpp

@@ -0,0 +1,63 @@
+#include "scopecalendarbtn.h"
+#include "calendarex.h"
+#include <QDebug>
+
+ScopeCalendarBtn::ScopeCalendarBtn(QWidget *parent) 
+    : QPushButton(parent)
+    , m_dtFrom(QDate::currentDate())
+    , m_dtTo(QDate::currentDate())
+    , m_dtMin(QDate(1,1,1))
+    , m_dtMax(QDate(9999,12,31))
+    , m_strFormat("%1 - %2")
+{
+    connect(this, &QPushButton::clicked, this, &ScopeCalendarBtn::OnPopBtnClicked);
+    SetScope(m_dtFrom, m_dtTo);
+}
+
+void ScopeCalendarBtn::SetMinimumDate(const QDate &date)
+{
+    m_dtMin = date;
+    m_dtMax = qMax(m_dtMin, m_dtMax);
+    m_dtFrom = qMax(m_dtMin, m_dtFrom);
+    m_dtTo = qMax(m_dtMin, m_dtTo);
+}
+
+void ScopeCalendarBtn::SetMiaxmumDate(const QDate &date)
+{
+    m_dtMax = date;
+    m_dtMin = qMin(m_dtMax, m_dtMin);
+    m_dtFrom = qMin(m_dtMax, m_dtFrom);
+    m_dtTo = qMin(m_dtMax, m_dtTo);
+}
+
+void ScopeCalendarBtn::SetScope(const QDate &from, const QDate &to)
+{
+    m_dtFrom = qBound(m_dtMin, from, m_dtMax);
+    m_dtTo = qBound(m_dtMin, to, m_dtMax);
+    QString text = QString(m_strFormat).arg(m_dtFrom.toString("yyyy-MM-dd"), m_dtTo.toString("yyyy-MM-dd"));
+    setText(text);
+}
+
+void ScopeCalendarBtn::OnPopBtnClicked()
+{
+    ScopedCalendar *pC = new ScopedCalendar(m_dtFrom, m_dtTo);
+    pC->show();
+    pC->positionCalendarPopup(this);
+    if(m_dtMin.isValid()) pC->SetMinimumDate(m_dtMin);
+    if(m_dtMax.isValid()) pC->SetMaximumDate(m_dtMax);
+    connect(pC, &ScopedCalendar::sig_ScopeSelected, this, &ScopeCalendarBtn::OnScopeSelected);
+}
+
+void ScopeCalendarBtn::OnScopeSelected(const QDate &from, const QDate &to)
+{
+    SetScope(from, to);
+    emit sig_ScopeSelected(from, to);
+}
+
+void ScopeCalendarBtn::paintEvent(QPaintEvent *event)
+{
+//    PainterEx painter(this);
+//    painter.SetBrushOnly(Qt::gray);
+//    painter.drawRect(rect());
+    QPushButton::paintEvent(event);
+}

+ 33 - 0
common/DateSelect/scopecalendarbtn.h

@@ -0,0 +1,33 @@
+#ifndef CALENDARSCOPEBTN_H
+#define CALENDARSCOPEBTN_H
+
+#include <QDateTime>
+#include <QPushButton>
+
+class ScopeCalendarBtn : public QPushButton
+{
+    Q_OBJECT
+public:
+    explicit ScopeCalendarBtn(QWidget *parent = nullptr);
+    QDate From(){return m_dtFrom;}
+    QDate To(){return m_dtTo;}
+    void SetMinimumDate(const QDate &date);
+    void SetMiaxmumDate(const QDate &date);
+    void SetScope(const QDate &from, const QDate &to);
+    void SetFormat(const QString &format = "%1 - %2"){m_strFormat = format;}
+signals:
+    void sig_ScopeSelected(const QDate &from, const QDate &to);
+private slots:
+    void OnPopBtnClicked();
+    void OnScopeSelected(const QDate &from, const QDate &to);
+protected:
+    void paintEvent(QPaintEvent *event);
+private:
+    QDate m_dtFrom;
+    QDate m_dtTo;
+    QDate m_dtMin;
+    QDate m_dtMax;
+    QString m_strFormat;
+};
+
+#endif // CALENDARSCOPEBTN_H

+ 34 - 0
common/DateSelect/scopeselectionmodel.cpp

@@ -0,0 +1,34 @@
+#include "scopeselectionmodel.h"
+
+ScopeSelectionModel::ScopeSelectionModel(QObject *parent)
+    : QObject(parent)
+    , bLocked(false)
+{
+    
+}
+
+void ScopeSelectionModel::Clear()
+{
+    dtFirst = QDate();
+    dtSecond = QDate();
+    bLocked = false;
+}
+
+void ScopeSelectionModel::Update()
+{
+    emit sig_Update();
+}
+
+void ScopeSelectionModel::Locked()
+{
+    QDate from = qMin(dtFirst, dtSecond);
+    QDate to = qMax(dtFirst, dtSecond);
+    emit sig_ScopeSelected(from, to);
+}
+
+//void DateScopeModel::Lock(const QDate &date)
+//{
+//    if(bLocked || !dtFirst.isValid()) return;
+//    dtSecond = date;
+//    bLocked = true;
+//}

+ 26 - 0
common/DateSelect/scopeselectionmodel.h

@@ -0,0 +1,26 @@
+#ifndef DATESCOPEMODEL_H
+#define DATESCOPEMODEL_H
+
+#include <QDateTime>
+#include <QObject>
+
+class ScopeSelectionModel : public QObject
+{
+    Q_OBJECT
+public:
+    explicit ScopeSelectionModel(QObject *parent = nullptr);
+    void Clear();
+    void Update();
+    void Locked();
+signals:
+    void sig_Update();
+    void sig_ScopeSelected(const QDate &from, const QDate &to);
+    //void Lock(const QDate &date);
+public:
+    //QPair<QDate, QDate> m_pairDateScope;    //鼠标移动时的范围指示, key和value都是Qdate类型, 但是不一定哪个较大
+    QDate dtFirst;
+    QDate dtSecond;
+    bool bLocked;
+};
+
+#endif // DATESCOPEMODEL_H

+ 232 - 0
common/FlowLayout/flowlayout.cpp

@@ -0,0 +1,232 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of The Qt Company Ltd nor the names of its
+**     contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWidgets>
+
+#include "flowlayout.h"
+//! [1]
+FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
+    : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
+{
+    setContentsMargins(margin, margin, margin, margin);
+}
+
+FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
+    : m_hSpace(hSpacing), m_vSpace(vSpacing)
+{
+    setContentsMargins(margin, margin, margin, margin);
+}
+//! [1]
+
+//! [2]
+FlowLayout::~FlowLayout()
+{
+    QLayoutItem *item;
+    while ((item = takeAt(0)))
+        delete item;
+}
+//! [2]
+
+//! [3]
+void FlowLayout::addItem(QLayoutItem *item)
+{
+    itemList.append(item);
+}
+//! [3]
+
+//! [4]
+int FlowLayout::horizontalSpacing() const
+{
+    if (m_hSpace >= 0) {
+        return m_hSpace;
+    } else {
+        return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
+    }
+}
+
+int FlowLayout::verticalSpacing() const
+{
+    if (m_vSpace >= 0) {
+        return m_vSpace;
+    } else {
+        return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
+    }
+}
+//! [4]
+
+//! [5]
+int FlowLayout::count() const
+{
+    return itemList.size();
+}
+
+QLayoutItem *FlowLayout::itemAt(int index) const
+{
+    return itemList.value(index);
+}
+
+QLayoutItem *FlowLayout::takeAt(int index)
+{
+    if (index >= 0 && index < itemList.size())
+        return itemList.takeAt(index);
+    else
+        return 0;
+}
+//! [5]
+
+//! [6]
+Qt::Orientations FlowLayout::expandingDirections() const
+{
+    return 0;
+}
+//! [6]
+
+//! [7]
+bool FlowLayout::hasHeightForWidth() const
+{
+    return true;
+}
+
+int FlowLayout::heightForWidth(int width) const
+{
+    int height = doLayout(QRect(0, 0, width, 0), true);
+    return height;
+}
+//! [7]
+
+//! [8]
+void FlowLayout::setGeometry(const QRect &rect)
+{
+    QLayout::setGeometry(rect);
+    doLayout(rect, false);
+}
+
+QSize FlowLayout::sizeHint() const
+{
+    return minimumSize();
+}
+
+QSize FlowLayout::minimumSize() const
+{
+    QSize size;
+    QLayoutItem *item;
+    foreach (item, itemList)
+        size = size.expandedTo(item->minimumSize());
+
+    size += QSize(2*margin(), 2*margin());
+    return size;
+}
+//! [8]
+
+/**
+ * @brief 布局item
+ * 
+ * @param rect 
+ * @param testOnly 
+ * @return int 
+ */
+int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
+{
+    int left, top, right, bottom;
+    /* 获取布局的margins */
+    getContentsMargins(&left, &top, &right, &bottom);
+    QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
+    int x = effectiveRect.x();
+    int y = effectiveRect.y();
+    int lineHeight = 0;
+//! [9]
+
+//! [10]
+    /* 挨个排列组件,如果被隐藏了,那么也去掉前面的spacing距离 */
+    QLayoutItem *item;
+    foreach (item, itemList) 
+    {
+        QWidget *wid = item->widget();
+        int spaceX = horizontalSpacing();
+        if (spaceX == -1)
+            spaceX = wid->style()->layoutSpacing(
+                QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
+        int spaceY = verticalSpacing();
+        if (spaceY == -1)
+            spaceY = wid->style()->layoutSpacing(
+                QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
+//! [10]
+//! [11]
+        int nextX = x + item->sizeHint().width() + spaceX;
+        if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
+            x = effectiveRect.x();
+            y = y + lineHeight + spaceY;
+            nextX = x + item->sizeHint().width() + spaceX;
+            lineHeight = 0;
+        }
+        /* 不是test就设置位置 */
+        if (!testOnly)
+            item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
+
+        x = nextX;
+        lineHeight = qMax(lineHeight, item->sizeHint().height());
+    }
+    return y + lineHeight - rect.y() + bottom;
+}
+//! [11]
+//! [12]
+int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
+{
+    QObject *parent = this->parent();
+    if (!parent) {
+        return -1;
+    } else if (parent->isWidgetType()) {
+        QWidget *pw = static_cast<QWidget *>(parent);
+        return pw->style()->pixelMetric(pm, 0, pw);
+    } else {
+        return static_cast<QLayout *>(parent)->spacing();
+    }
+}
+//! [12]

+ 91 - 0
common/FlowLayout/flowlayout.h

@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of The Qt Company Ltd nor the names of its
+**     contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FLOWLAYOUT_H
+#define FLOWLAYOUT_H
+
+#include <QLayout>
+#include <QRect>
+#include <QStyle>
+//! [0]
+class FlowLayout : public QLayout
+{
+public:
+    explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
+    explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
+    ~FlowLayout();
+
+    void addItem(QLayoutItem *item) override;
+    int horizontalSpacing() const;
+    int verticalSpacing() const;
+    Qt::Orientations expandingDirections() const override;
+    bool hasHeightForWidth() const override;
+    int heightForWidth(int) const override;
+    int count() const override;
+    QLayoutItem *itemAt(int index) const override;
+    QSize minimumSize() const override;
+    void setGeometry(const QRect &rect) override;
+    QSize sizeHint() const override;
+    QLayoutItem *takeAt(int index) override;
+    void setHorizontalSpacing(int hSpace) { m_hSpace = hSpace; }
+    void setVerticalSpacing(int vSpace) { m_vSpace = vSpace; }
+
+private:
+    /* 布局排列 */
+    int doLayout(const QRect &rect, bool testOnly) const;
+    int smartSpacing(QStyle::PixelMetric pm) const;
+
+    QList<QLayoutItem *> itemList;
+    int m_hSpace;
+    int m_vSpace;
+};
+//! [0]
+
+#endif // FLOWLAYOUT_H

+ 1 - 0
common/FlowLayout/说明.txt

@@ -0,0 +1 @@
+流动布局,从左到右排列,左边的控件隐藏后,右边的会自动往左移

+ 0 - 7
common/Shadow/OneShadowEffect.h

@@ -3,13 +3,6 @@
 
 #include <QGraphicsDropShadowEffect>
 
-/**
- * @brief 使用方式:
-            1、创建实例,设置阴影半径,如:OneShadowEffect *shadow = new OneShadowEffect(this, 20);
-            2、给需要添加阴影的控件设置setGraphicsEffect(show);
- */
-
-
 class OneShadowEffect : public QGraphicsDropShadowEffect
 {
 

+ 54 - 0
common/TimeSelect/QSS/timepartwidget.qss

@@ -0,0 +1,54 @@
+QListWidget
+{
+	font-family:"Source Han Sans CN";
+    font-size: 14px;
+    font-weight: 400;
+    color: "#3A3F63";
+	border: none;
+	border-right: 1px solid rgba(0, 0, 0, 0.06);
+	background: #ffffff;
+	outline: 0px;
+}
+QListView::item:hover 
+{
+	background-color: rgb(245,245,245);
+	font-family:"Source Han Sans CN";
+    font-size: 14px;
+    font-weight: 400;
+    color: "#3A3F63";
+}
+
+QListView::item:selected 
+{
+	background-color: rgb(227, 238, 255);
+	font-family:"Source Han Sans CN";
+    font-size: 14px;
+    font-weight: 500;
+    color: "#3A3F63";
+	outline: none;
+}
+
+QScrollBar:vertical
+{
+	border:none;
+    background-color: #e2e2e2;
+    width: 4px;
+}
+
+QScrollBar::handle:vertical
+{
+	background: #e2e2e2;
+    border-radius: 2px;
+}
+
+QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical
+{
+	background: #ffffff;/*transparent;*/
+    border: none;
+}
+
+QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical
+{
+	background: #ffffff;/*transparent;*/
+    border: none;
+}

+ 12 - 0
common/TimeSelect/QSS/timewidget.qss

@@ -0,0 +1,12 @@
+QPushButton
+{
+	border-image: url(:/images/datetime.png);
+}
+QPushButton[selected=true]
+{
+	border-image: url(:/images/datetime.png);
+}
+QPushButton::hover[selected=true]
+{
+	border-image: url(:/images/cancle.png);
+}

+ 25 - 0
common/TimeSelect/mytimedelegate.cpp

@@ -0,0 +1,25 @@
+#include "mytimedelegate.h"
+#include <QPainter>
+#include <QMouseEvent>
+
+MyTimeDelegate::MyTimeDelegate(QObject* parent) :
+    QStyledItemDelegate(parent)
+{
+
+}
+
+void MyTimeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+    if (nullptr == painter || !index.isValid()) {
+        return;
+    }
+    QString data = index.data().toString();
+    if (data.isEmpty()) {
+        if ((option.state & QStyle::State_MouseOver) ||
+                (option.state & QStyle::State_Selected)) {
+            painter->fillRect(option.rect, c_bkColor);
+            return;
+        }
+    }
+    QStyledItemDelegate::paint(painter, option, index);
+}

+ 16 - 0
common/TimeSelect/mytimedelegate.h

@@ -0,0 +1,16 @@
+#ifndef MYTIMEDELEGATE_H
+#define MYTIMEDELEGATE_H
+
+#include <QStyledItemDelegate>
+
+class MyTimeDelegate : public QStyledItemDelegate
+{
+    Q_OBJECT
+public:
+    const QColor c_bkColor = QColor(255,255,255);
+    explicit MyTimeDelegate(QObject *parent = nullptr);
+protected:
+     void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+};
+
+#endif // MYTIMEDELEGATE_H

+ 40 - 0
common/TimeSelect/shadowwidget.cpp

@@ -0,0 +1,40 @@
+#include "shadowwidget.h"
+#include <QLayout>
+#include <QGraphicsDropShadowEffect>
+#include <QDebug>
+
+ShadowWidget::ShadowWidget(QWidget *parent)
+    : QWidget(parent)
+    , m_pCentralWidget(new QWidget(this))
+{
+    setWindowFlags(Qt::FramelessWindowHint);
+    setAttribute(Qt::WA_TranslucentBackground);
+
+    QHBoxLayout* pLayout = new QHBoxLayout();
+    if (nullptr != pLayout) {
+        pLayout->addWidget(m_pCentralWidget);
+        this->setLayout(pLayout);
+    }
+
+    QGraphicsDropShadowEffect *pShadowEffect = new QGraphicsDropShadowEffect(this);
+    pShadowEffect->setBlurRadius(16);               // 模糊度
+    pShadowEffect->setColor(QColor(0, 0, 0, 90));   // 阴影的颜色
+    pShadowEffect->setOffset(0, 0);                 // 水平和垂直偏移量
+    m_pCentralWidget->setGraphicsEffect(pShadowEffect);
+}
+
+
+void ShadowWidget::setCentralLayout(QLayout *layout)
+{
+    if (nullptr != m_pCentralWidget) {
+        m_pCentralWidget->setLayout(layout);
+    }
+}
+
+QLayout* ShadowWidget::getLayout() const
+{
+    if (nullptr != m_pCentralWidget) {
+        return m_pCentralWidget->layout();
+    }
+    return nullptr;
+}

+ 20 - 0
common/TimeSelect/shadowwidget.h

@@ -0,0 +1,20 @@
+#ifndef SHADOWWIDGET_H
+#define SHADOWWIDGET_H
+
+#include <QWidget>
+
+class ShadowWidget : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit ShadowWidget(QWidget *parent = nullptr);
+
+    QWidget* centralWidget() {return m_pCentralWidget;}
+    void setCentralLayout(QLayout* layout);
+    QLayout* getLayout() const;
+signals:
+private:
+    QWidget* m_pCentralWidget{nullptr};
+};
+
+#endif // SHADOWWIDGET_H

+ 177 - 0
common/TimeSelect/timepartwidget.cpp

@@ -0,0 +1,177 @@
+#include "timepartwidget.h"
+#include "ui_timepartwidget.h"
+#include <QListWidgetItem>
+#include <QScrollBar>
+#include <QDateTime>
+#include <QMouseEvent>
+#include <QDebug>
+#include <QFile>
+#include "mytimedelegate.h"
+// #include "lhstylemanager.h"
+// #include "LHQLogApi.h"
+#include "UIStyleManager.h"
+
+TimePartWidget::TimePartWidget(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::TimePartWidget)
+{
+    ui->setupUi(this);
+}
+
+TimePartWidget::TimePartWidget(TimePartWidget::emSection type, QWidget* parent) :
+    TimePartWidget(parent)
+{
+    m_type = type;
+    // 设置代理
+    m_delegate.reset(new MyTimeDelegate(this));
+    if (nullptr != m_delegate.data()) {
+        ui->listWidget->setItemDelegate(m_delegate.data());
+    }
+
+    // 设置滚动条
+    m_pListBar.reset(new QScrollBar(Qt::Orientation::Vertical, ui->listWidget));
+    if (nullptr != m_pListBar.data()) {
+        m_pListBar->move(width() - 4, 1); // 宽度4,与右侧距离2
+        m_pListBar->setMinimumHeight(this->height() - 2);
+        m_pListBar->setMaximumHeight(this->height() - 2);
+        m_pListBar->hide();
+        connect(ui->listWidget->verticalScrollBar(), &QScrollBar::valueChanged, m_pListBar.data(), &QScrollBar::setValue);
+        connect(ui->listWidget->verticalScrollBar(), &QScrollBar::rangeChanged, m_pListBar.data(), &QScrollBar::setRange);
+        connect(m_pListBar.data(), &QScrollBar::valueChanged, ui->listWidget->verticalScrollBar(), &QScrollBar::setValue);
+    }
+    /* 设置QSS */
+    setQSS();
+
+    switch (type) {
+    case MINUTE:
+    case SECOND: {
+        InitListWidget(60);
+        break;
+    }
+    case HOUR: {
+        InitListWidget(24);
+        break;
+    }
+    default:
+        break;
+    }
+    ui->listWidget->setCurrentRow(0);
+    ui->listWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+    auto pf = [this](QListWidgetItem* item){
+        ui->listWidget->scrollToItem(item, QAbstractItemView::PositionAtTop); // 滚到最顶部
+    };
+    connect(ui->listWidget, &QListWidget::itemPressed, this, pf);
+    connect(ui->listWidget, &QListWidget::itemPressed, this, &TimePartWidget::sigItemClicked);
+}
+
+TimePartWidget::~TimePartWidget()
+{
+    delete ui;
+}
+
+void TimePartWidget::SetTime(const QDateTime &dt)
+{
+    switch (m_type) {
+    case HOUR:
+        SelectListItem(dt.time().hour());
+        break;
+    case MINUTE:
+        SelectListItem(dt.time().minute());
+        break;
+    case SECOND:
+        SelectListItem(dt.time().second());
+        break;
+    default:
+        break;
+    }
+}
+
+void TimePartWidget::SetMaxWidth(int w)
+{
+    // 更新滚动条位置
+    if (!m_pListBar.isNull()) {
+        m_pListBar->move(w - 4, 1);
+    }
+}
+
+void TimePartWidget::ScrollToSelect()
+{
+    auto items = ui->listWidget->selectedItems();
+    if (items.count() > 0) {
+        ui->listWidget->scrollToItem(items.at(0), QAbstractItemView::PositionAtTop);
+    }
+}
+
+/* 设置QSS */
+void TimePartWidget::setQSS()
+{
+    // QString appPath = QCoreApplication::applicationDirPath();
+    QString qssPath = EPUIStyle.getQSSPath() + "/SelectTime/timepartwidget.qss";
+
+    QFile file(qssPath);
+    if(file.open(QFile::ReadOnly))
+    {
+        QString styleSheet = QLatin1String(file.readAll());
+        setStyleSheet(styleSheet);
+        file.close();
+    }
+    // else 
+    // {
+    //     LH_WRITE_ERROR("open qss file failed: " + qssPath);
+    // }
+}
+
+void TimePartWidget::enterEvent(QEvent *event)
+{
+    if (!m_pListBar.isNull()) {
+        m_pListBar->show();
+    }
+
+    QWidget::enterEvent(event);
+}
+
+void TimePartWidget::leaveEvent(QEvent *event)
+{
+    if (!m_pListBar.isNull()) {
+        m_pListBar->hide();
+    }
+    QWidget::leaveEvent(event);
+}
+
+void TimePartWidget::InitListWidget(int total)
+{
+    ui->listWidget->clear();
+    for (int i = 0; i < total; ++i) {
+        QListWidgetItem* pItem = new QListWidgetItem;
+        if (nullptr == pItem) {
+            continue;
+        }
+        pItem->setTextAlignment(Qt::AlignCenter);
+        pItem->setText(QString("%1").arg(i, 2, 10, QLatin1Char('0')));
+        pItem->setSizeHint(QSize(32, 32));
+        ui->listWidget->addItem(pItem);
+    }
+    // 填充4个不可选空白项
+    for (int i = 0; i < 4; ++i) {
+        QListWidgetItem* pItem = new QListWidgetItem;
+        if (nullptr == pItem) continue;
+        pItem->setSizeHint(QSize(32, 32));
+        pItem->setFlags(pItem->flags() & ~Qt::ItemIsEnabled & ~Qt::ItemIsSelectable);
+        ui->listWidget->addItem(pItem);
+    }
+}
+
+void TimePartWidget::SelectListItem(int time)
+{
+    for (int i = 0; i < ui->listWidget->count(); ++i) {
+        QListWidgetItem* pItem = ui->listWidget->item(i);
+        if (nullptr == pItem) continue;
+        // 注意时间填充了0
+        if (pItem->text() == QString("%1").arg(time, 2, 10, QLatin1Char('0'))) {
+            //qInfo() << "SelectListItem: " << "滚动到最顶部, time: " << time;
+            pItem->setSelected(true);
+            ui->listWidget->scrollToItem(pItem, QAbstractItemView::PositionAtTop); // 滚到最顶部
+            break;
+        }
+    }
+}

+ 52 - 0
common/TimeSelect/timepartwidget.h

@@ -0,0 +1,52 @@
+#ifndef TIMEPARTWIDGET_H
+#define TIMEPARTWIDGET_H
+
+#include <QWidget>
+
+class MyTimeDelegate;
+class QListWidgetItem;
+class QScrollBar;
+
+namespace Ui {
+class TimePartWidget;
+}
+
+class TimePartWidget : public QWidget
+{
+    Q_OBJECT
+public:
+    enum emSection {
+        HOUR = 0,
+        MINUTE = 1,
+        SECOND = 2,
+        MAX_SECTION
+    };
+public:
+    explicit TimePartWidget(QWidget *parent = nullptr);
+    TimePartWidget(emSection type, QWidget* parent = nullptr);
+    ~TimePartWidget();
+
+    emSection GetType() const {return m_type;}
+    void SetTime(const QDateTime& dt);
+    void SetMaxWidth(int w);
+    void ScrollToSelect();
+
+    /* 设置QSS */
+    void setQSS();
+
+protected:
+    void enterEvent(QEvent* event) override;
+    void leaveEvent(QEvent* event) override;
+signals:
+    void sigItemClicked(QListWidgetItem* item);
+private:
+    void InitListWidget(int total);
+    void SelectListItem(int time);
+private:
+    Ui::TimePartWidget *ui;
+    emSection m_type;
+    QScopedPointer<MyTimeDelegate> m_delegate;
+    QScopedPointer<QScrollBar> m_pListBar;
+};
+
+#endif // TIMEPARTWIDGET_H

+ 61 - 0
common/TimeSelect/timepartwidget.ui

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TimePartWidget</class>
+ <widget class="QWidget" name="TimePartWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>116</width>
+    <height>192</height>
+   </rect>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>16777215</width>
+    <height>16777215</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>0</number>
+   </property>
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QListWidget" name="listWidget">
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>16777215</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="styleSheet">
+      <string notr="true"/>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 502 - 0
common/TimeSelect/timewidget.cpp

@@ -0,0 +1,502 @@
+#include "timewidget.h"
+#include "ui_timewidget.h"
+#include <QListWidgetItem>
+#include <QMouseEvent>
+#include <QDebug>
+#include <QSizePolicy>
+#include <QFile>
+#include "timepartwidget.h"
+#include "shadowwidget.h"
+#include "UIStyleManager.h"
+
+TimeWidget::TimeWidget(QWidget *parent , ShowType type) :
+    QFrame(parent),
+    ui(new Ui::TimeWidget),
+    m_wdgTimeArea(nullptr),
+    m_pMainWindow(parent),
+    m_type(type)
+{
+    ui->setupUi(this);
+    Init();
+}
+
+
+TimeWidget::TimeWidget(ShowType type) : 
+    QFrame(nullptr),
+    ui(new Ui::TimeWidget),
+    m_wdgTimeArea(nullptr),
+    m_pMainWindow(nullptr),
+    m_type(type)
+{
+    ui->setupUi(this);
+    Init();
+}
+
+TimeWidget::~TimeWidget()
+{
+    delete ui;
+}
+
+void TimeWidget::CreateTimeVector(const QVector<int> &types)
+{
+    ClearVector(m_vecTimePart);
+    for (auto& type : types) {
+        TimePartWidget::emSection emType = static_cast<TimePartWidget::emSection>(type);
+        if (emType >= TimePartWidget::emSection::MAX_SECTION) {
+            continue;
+        }
+        TimePartWidget* pTmp = new TimePartWidget(emType, this);
+        if (nullptr != pTmp) {
+            pTmp->SetMaxWidth(width() / types.count());
+            m_vecTimePart.append(pTmp);
+        }
+    }
+}
+
+void TimeWidget::ClearVector(QVector<TimePartWidget *> &vec)
+{
+    for (auto& pWdg : vec) {
+        if (nullptr != pWdg) {
+            delete pWdg;
+            pWdg = nullptr;
+        }
+    }
+    vec.clear();
+}
+
+void TimeWidget::SetMainWindow(QWidget* pWidget)
+{
+    if (nullptr != m_pMainWindow) {
+        this->removeEventFilter(m_pMainWindow);
+    }
+    if (nullptr != pWidget) {
+        pWidget->installEventFilter(this);
+    }
+    m_pMainWindow = pWidget;
+}
+
+/* 返回时间 */
+QTime TimeWidget::getTime()
+{
+    return ui->dateTimeEdit->time();
+}
+
+
+/**
+ * @brief 存在时间就返回hh:mm:ss.zzz格式字符串,否则返回提示信息
+ * @return
+ */
+QString TimeWidget::getTimeStr()
+{
+    QString ret(ui->lbl_tip->text());
+    if (!ui->dateTimeEdit->isHidden()) {
+        ret = ui->dateTimeEdit->time().toString("hh:mm:ss");//.zzz
+    }
+    return ret;
+}
+/**
+ * @brief 存在返回时间,否则返回00:00:00
+ * @return
+ */
+QTime TimeWidget::getFormTime() const
+{
+    return ui->dateTimeEdit->isHidden() ? QTime(0, 0, 0) : ui->dateTimeEdit->time();
+}
+
+void TimeWidget::setTime(const QString& t)
+{
+    QTime time = QTime::fromString(t, "hh:mm:ss");
+    ui->dateTimeEdit->setTime(time);
+    ui->lbl_tip->hide();
+    ui->dateTimeEdit->show();
+    UpdatePopupTime(ui->dateTimeEdit->dateTime());//
+}
+
+void TimeWidget::setTime(const QTime& t)
+{
+    ui->dateTimeEdit->setTime(t);
+    ui->lbl_tip->hide();
+    ui->dateTimeEdit->show();
+    UpdatePopupTime(ui->dateTimeEdit->dateTime());
+}
+
+void TimeWidget::clearTime()
+{
+    ui->dateTimeEdit->setTime(QTime(0, 0, 0));
+    ui->dateTimeEdit->hide();
+    ui->lbl_tip->show();
+}
+
+QString TimeWidget::tipText() const
+{
+    return ui->lbl_tip->text();
+}
+/* 设置时间选择区域的大小,不能超过时间编辑栏的大小,这个主要是防止时间栏太宽,影响美观 */
+void TimeWidget::setTimeAreaWidth(int w)
+{
+    if(w < 0)
+    {
+        m_width = 0;
+    }
+    else if(w > this->width())
+    {
+        m_width = this->width();
+    }
+    else
+    {
+        m_width = w;
+    }
+}
+
+void TimeWidget::showTimeEditArea()
+{
+    this->show();
+    ShowTimeArea(true);
+}
+
+/* 以弹窗的模式模态显示 */
+void TimeWidget::execShow()
+{
+    QEventLoop loop;
+    connect (this, &TimeWidget::signal_close, &loop, &QEventLoop::quit);
+    this->show();
+    ShowTimeArea(true);
+    loop.exec();
+
+    deleteLater();
+}
+
+/* 设置时间图标 */
+void TimeWidget::setIcon(const QString& icon)
+{
+    /* 设置图片适应按钮大小 */
+    QString ss = QString("border-image: url(%1)").arg(icon);
+    ui->btn_tip->setStyleSheet(ss);
+    ui->btn_tip->show();
+}
+
+/* 设置图标显示 */
+void TimeWidget::setIconShow(bool isShow)
+{
+    if(isShow)
+    {
+        ui->btn_tip->show();
+    }else {
+        ui->btn_tip->hide();
+    }
+}
+
+/* 设置图标大小 */
+void TimeWidget::setIconSize(int w, int h)
+{
+    ui->btn_tip->setMinimumSize(w, h);
+    /* 设置为固定大小 */
+    ui->btn_tip->setFixedSize(w, h);
+    // ui->btn_tip->resize(w, h);
+}
+
+/* 设置默认的样式 */
+void TimeWidget::setDefaultStyle()
+{
+    /* 判断显示类型,如果是弹窗直接显示编辑区 */
+    if(m_type == Dialog)
+    {
+        ui->btn_tip->hide();
+        this->resize(136,36);
+//        ShowTimeArea(false);
+        /* 设置编辑栏样式 */
+        // this->setStyleSheet(R"(
+        //     TimeWidget
+        //     {
+        //         padding-left:15px;
+        //         background: #FFFFFF;
+        //         border-radius: 4px;
+        //         border: 1px solid #E6E9F4;
+        //     }
+        // )");
+    }
+}
+
+/* 设置编辑栏大小 */
+void TimeWidget::setEditLine(int w, int h)
+{
+    this->resize(w, h);
+}
+
+/* 设置QSS */
+void TimeWidget::setQSS()
+{
+    QString qssPath = EPUIStyle.getQSSPath() + "/SelectTime/timewidget.qss";
+
+    QFile file(qssPath);
+    if(file.open(QFile::ReadOnly))
+    {
+        QString styleSheet = QLatin1String(file.readAll());
+        setStyleSheet(styleSheet);
+        file.close();
+    }
+    // else 
+    // {
+    //     LH_WRITE_ERROR("open qss file failed: " + qssPath);
+    // }
+}
+
+/**
+ * @brief 点击提示信息
+ */
+void TimeWidget::onBtnTipClicked()
+{
+    bool isSelected = ui->btn_tip->property("selected").toBool();
+    if (!isSelected) {
+        // 显示日期
+        ui->lbl_tip->hide();
+        ui->dateTimeEdit->show();
+        ShowTimeArea(true);
+    } else {
+        // 清除时间
+        ui->dateTimeEdit->setTime(QTime(0, 0, 0));
+        UpdateProperty(ui->btn_tip, "selected", false);
+        QDateTime dt;
+        dt.setTime(QTime(0, 0, 0));
+        UpdatePopupTime(dt);
+        ShowTimeArea(false);
+        ui->dateTimeEdit->hide();
+        ui->lbl_tip->show();
+    }
+}
+
+/**
+ * @brief 这里添加了两个信号,一个是修改过的新时间,一个是旧时间
+ * @param obj
+ * @param e
+ * @return
+ */
+bool TimeWidget::eventFilter(QObject* obj, QEvent* e)
+{
+    if (obj == ui->dateTimeEdit) {
+        if (e->type() == QEvent::FocusIn && m_type == EditLine)
+        {
+            //qInfo() << "dateTimeEdit focusIn";
+            ShowTimeArea(true);
+            UpdateProperty(ui->btn_tip, "selected", true);
+            emit signal_formerTimer(ui->dateTimeEdit->time());
+        }
+        return QWidget::eventFilter(obj, e);
+    } else if (obj == ui->lbl_tip) {
+        if (e->type() == QEvent::MouseButtonPress && m_type == EditLine)
+        {
+            //qInfo() << "mouseButtonPress";
+            ui->dateTimeEdit->show();
+            ui->lbl_tip->hide();
+            ShowTimeArea(true);
+            //ui->dateTimeEdit->setFocus();//
+            return QWidget::eventFilter(obj, e);
+        }
+    } else if (obj == this) {
+        if (e->type() == QEvent::Enter) {
+            UpdateProperty(this, "hover", true);
+        } else if (e->type() == QEvent::Leave &&
+                   ((m_wdgTimeArea.isNull() && !ui->dateTimeEdit->hasFocus()) ||
+                    (m_wdgTimeArea && m_wdgTimeArea->isHidden())) ) {
+            UpdateProperty(this, "hover", false);
+        }
+    }
+    /* 判断是不是显示区外面,是的话就隐藏 */
+    QMouseEvent* pMouse = reinterpret_cast<QMouseEvent*>(e);
+    if (nullptr != pMouse) {
+        if (pMouse->type() == QEvent::MouseButtonPress) {
+            //qInfo() << "focusOut";
+            QPoint gtl = this->mapToGlobal(rect().topLeft());
+            QRect rc(gtl.x(), gtl.y(), width(), height()); // 全局位置判断
+            if (!rc.contains(pMouse->globalPos())) {
+                ShowTimeArea(false);
+                ui->dateTimeEdit->clearFocus();
+                UpdateProperty(this, "hover", false);
+                /* 关闭显示,发送携带时间的信号 */
+                emit signal_nowTime(ui->dateTimeEdit->time());
+                if(m_type == Dialog)
+                {
+                    this->close();
+                }
+            }
+        }
+    }
+
+    return QWidget::eventFilter(obj, e);
+}
+/**
+ * @brief m_wdgTimeArea跟随时间栏移动
+ * @param event
+ */
+void TimeWidget::moveEvent(QMoveEvent *event)
+{
+    if(m_type == Dialog && m_wdgTimeArea != nullptr)
+    {
+        QPoint pt = this->mapTo(m_pMainWindow, QPoint(0, 0));
+        m_wdgTimeArea->move(QPoint(pt.x() - SHADOW_MARGIN, pt.y() + this->height()));
+//        qDebug() << "posX:" << pt.x() << "posY:" << pt.y();
+    }
+}
+/**
+ * @brief 时间列表选中事件
+ * @param item
+ */
+void TimeWidget::onListItemClicked(QListWidgetItem *item)
+{
+    if (nullptr == item) return;
+    TimePartWidget* pWdg = reinterpret_cast<TimePartWidget*>(QObject::sender());
+    if (nullptr == pWdg) return;
+
+    QString data(item->text());
+    if (data.isEmpty()) return; // 过滤空白项
+    QDateTime oldDt(ui->dateTimeEdit->dateTime());
+    QTime t(ui->dateTimeEdit->time());
+    switch (pWdg->GetType()) {
+    case TimePartWidget::HOUR: {
+        t.setHMS(data.toInt(), t.minute(), t.second());
+        break;
+    }
+    case TimePartWidget::MINUTE: {
+        t.setHMS(t.hour(), data.toInt(), t.second());
+        break;
+    }
+    case TimePartWidget::SECOND: {
+        t.setHMS(t.hour(), t.minute(), data.toInt());
+        break;
+    }
+    default:
+        break;
+    }
+    if (oldDt.time() != t) {
+        m_bTimeFlag = true;
+        ui->dateTimeEdit->setTime(t);
+    }
+}
+/**
+ * @brief QDateTimeEdit控件时间改变事件
+ * @param dt
+ */
+void TimeWidget::onDateTimeChanged(const QDateTime& dt)
+{
+    if (dt.time() != QTime(0, 0, 0)) {
+        UpdateProperty(ui->btn_tip, "selected", true);
+    }
+    // 同步到popupWidget
+    if (!m_bTimeFlag) {
+        UpdatePopupTime(dt);
+    }
+    m_bTimeFlag = false;
+}
+
+void TimeWidget::UpdateProperty(QObject* obj, const char *name, bool flag)
+{
+    if (nullptr == obj || nullptr == name) {
+        return;
+    }
+    obj->setProperty(name, flag);
+    QWidget* pWdg = qobject_cast<QWidget*>(obj);
+    if (nullptr != pWdg) {
+        this->style()->unpolish(pWdg);
+        this->style()->polish(pWdg);
+    }
+}
+/**
+ * @brief 更新popup列表选中时间
+ * @param dt
+ */
+void TimeWidget::UpdatePopupTime(const QDateTime& dt)
+{
+    // 如果时间列表还没初始化就不会设置时间了
+    for (int i = 0; i < m_vecTimeSections.size(); ++i) {
+        auto type = m_vecTimeSections.at(i);
+        TimePartWidget::emSection emType = static_cast<TimePartWidget::emSection>(type);
+        if (emType >= TimePartWidget::emSection::MAX_SECTION) {
+            continue;
+        }
+        if (i < m_vecTimePart.size()) {
+            TimePartWidget* pWdg = m_vecTimePart.at(i);
+            if (nullptr == pWdg) continue;
+            pWdg->SetTime(dt);
+        }
+    }
+}
+
+/**
+ * @brief wdgTimeArea区域是创建出来的,不属于ui区域,他的父类是m_pMainWindow,因此移动的时候需要使用m_pMainWindow的坐标
+ * @param bShow
+ */
+void TimeWidget::ShowTimeArea(bool bShow)
+{
+    if (m_wdgTimeArea.isNull()) {
+        CreatePopupWidget();
+    }
+    if (!m_wdgTimeArea.isNull()) {
+        if (bShow) {
+            //UpdatePopupTime(ui->dateTimeEdit->dateTime());
+            // 重新定位再显示
+            QPoint pt = this->mapTo(m_pMainWindow, QPoint(0, 0));
+            m_wdgTimeArea->move(QPoint(pt.x() - SHADOW_MARGIN, pt.y() + this->height()));
+            /* 设置选择条的大小,如果没有设置m_width,就是用时间编辑栏的宽度 */
+            m_wdgTimeArea->resize((m_width == 0 ? width() : m_width) + 2 * SHADOW_MARGIN, TIME_AREA_HEIGHT * 6 + 2 * SHADOW_MARGIN);
+            m_wdgTimeArea->setMaximumWidth(width() + 2 * SHADOW_MARGIN);
+            m_wdgTimeArea->show();
+            UpdatePopupTime(ui->dateTimeEdit->dateTime());
+        } else {
+            m_wdgTimeArea->hide();
+            emit signal_close();
+        }
+    }
+}
+
+void TimeWidget::CreatePopupWidget()
+{
+    // CreateTimeArea
+    m_vecTimeSections = {TimePartWidget::HOUR, TimePartWidget::MINUTE, TimePartWidget::SECOND};
+    CreateTimeVector(m_vecTimeSections);
+    m_wdgTimeArea.reset(new ShadowWidget(m_pMainWindow));
+    if (!m_wdgTimeArea.isNull()) {
+        if (m_wdgTimeArea->centralWidget() != nullptr) {
+            m_wdgTimeArea->centralWidget()->setObjectName(QLatin1String("wdg_TimeArea"));
+            m_wdgTimeArea->centralWidget()->setStyleSheet("QWidget#wdg_TimeArea{border-radius: 2px;border: none; }");
+        }
+        QHBoxLayout* hLayout = new QHBoxLayout();
+        hLayout->setMargin(1);
+        hLayout->setSpacing(0);
+        m_wdgTimeArea->setCentralLayout(hLayout);
+        if (nullptr == m_wdgTimeArea->getLayout()) {
+            delete hLayout;
+            hLayout = nullptr;
+        }
+        m_wdgTimeArea->resize(QSize(width() + 2 * SHADOW_MARGIN, TIME_AREA_HEIGHT * 6 + 2 * SHADOW_MARGIN));
+        m_wdgTimeArea->setMaximumWidth(width() + 2 * SHADOW_MARGIN);
+        m_wdgTimeArea->hide();
+
+        if (nullptr != m_wdgTimeArea->getLayout()) {
+            foreach (auto wdg, m_vecTimePart) {
+                m_wdgTimeArea->getLayout()->addWidget(wdg);
+                connect(wdg, &TimePartWidget::sigItemClicked, this, &TimeWidget::onListItemClicked);
+            }
+        }
+    }
+}
+
+/* 初始化函数 */
+void TimeWidget::Init()
+{
+    /* 设置QSS */
+    setQSS();
+    // InitUI
+    ui->dateTimeEdit->hide();
+    ui->dateTimeEdit->installEventFilter(this);
+    this->installEventFilter(this);
+    ui->lbl_tip->installEventFilter(this);
+    if (nullptr != m_pMainWindow) {
+        m_pMainWindow->installEventFilter(this);
+    }
+    ui->btn_tip->setProperty("selected", false);
+
+    connect(ui->btn_tip, &QPushButton::clicked, this, &TimeWidget::onBtnTipClicked);
+    connect(ui->dateTimeEdit, &QDateTimeEdit::dateTimeChanged, this, &TimeWidget::onDateTimeChanged);
+
+    setDefaultStyle();
+}

+ 112 - 0
common/TimeSelect/timewidget.h

@@ -0,0 +1,112 @@
+#ifndef TIMEWIDGET_H
+#define TIMEWIDGET_H
+
+
+
+/**
+ * 使用说明
+ *  1、这个时间选择器支持两种方式
+ *      * 提升QTimeEdit,作为编辑栏修改时间
+ *      * 以弹窗的方式出现
+ *  2、时间选择区域m_wdgTimeArea和时间编辑栏(在UI中)不是统一的,时间选择区域可以点击时间编辑栏
+ *     创建出来,也可以在弹窗中直接显示出来,所以moveEvent事件就是用来移动m_wdgTimeArea的。
+ *  3、在原来的基础上新添加了两个信号,在关闭的时候发送
+ *  4、使用Dialog模式的时候,点击空白处隐藏就会close掉,然后发送新的时间信号
+ *  5、使用Dialog模式,执行execShow()函数,会阻塞运行,直到关闭
+*/
+
+
+
+#include <QFrame>
+#include <QTime>
+
+class TimePartWidget;
+class QListWidgetItem;
+class ShadowWidget;
+
+namespace Ui {
+class TimeWidget;
+}
+
+/* 添加ShowType类型,判断这个控件是编辑栏还是弹窗 */
+class TimeWidget : public QFrame
+{
+    Q_OBJECT
+
+public:
+    enum ShowType{
+        EditLine = 0,           /* 时间编辑栏 */
+        Dialog = 1,             /* 以弹窗的形式出现 */
+    };
+    explicit TimeWidget(QWidget *parent = nullptr , ShowType type = EditLine);
+    explicit TimeWidget(ShowType type = EditLine);
+    ~TimeWidget();
+
+    void CreateTimeVector(const QVector<int>& types);
+    void ClearVector(QVector<TimePartWidget*>& vec);
+    // 在父窗口无法容纳控件时,这是必要的
+    void SetMainWindow(QWidget* pWidget);
+    QTime getTime();
+    QString getTimeStr();
+    QTime getFormTime() const;
+    void setTime(const QString& t);
+    void setTime(const QTime& t);
+    void clearTime();
+    QString tipText() const;
+    /* 新增一个设置时间条宽度的函数 */
+    void setTimeAreaWidth(int w);
+    /***** 2024-05-25 添加两个信号 ******/
+    void showTimeEditArea();
+    /* 以弹窗的模式模态显示 */
+    void execShow();
+    /* 设置时间图标 */
+    void setIcon(const QString& icon);
+    /* 设置图标显示 */
+    void setIconShow(bool isShow);
+    /* 设置图标大小 */
+    void setIconSize(int w, int h);
+    /* 设置默认的样式 */
+    void setDefaultStyle();
+    /* 设置编辑栏大小 */
+    void setEditLine(int w, int h);
+
+    /* 设置QSS */
+    void setQSS();
+signals:
+    void signal_nowTime(const QTime& time);
+    void signal_formerTimer(const QTime& time);
+    void signal_close();
+
+protected:
+    bool eventFilter(QObject* obj, QEvent* e) override;
+    void moveEvent(QMoveEvent *event) override;
+
+
+private slots:
+    void onBtnTipClicked();
+    void onListItemClicked(QListWidgetItem* item);
+    void onDateTimeChanged(const QDateTime& dt);
+private:
+    void UpdateProperty(QObject* obj, const char* name, bool flag);
+    void UpdatePopupTime(const QDateTime& dt);
+    void ShowTimeArea(bool bShow);
+    void CreatePopupWidget();
+    /* 初始化函数 */
+    void Init();
+private:
+    const int TIME_AREA_WIDTH = 56;
+    const int TIME_AREA_HEIGHT = 32;
+    const int SHADOW_MARGIN = 9;    // 对应BlurRadius模糊半径16px
+
+    Ui::TimeWidget *ui;
+    QVector<TimePartWidget*> m_vecTimePart;
+    QVector<int> m_vecTimeSections;
+    bool m_bTimeFlag{false};                     // 时间更新标志
+    QScopedPointer<ShadowWidget> m_wdgTimeArea;  // 时间选择窗口
+    QWidget* m_pMainWindow;                      // 外层祖辈窗口,能容纳时间控件高度即可(默认父窗口)
+    ShowType m_type;                            /* 显示类型 */
+    int m_width = 0;                            /* TimeArea宽度 */
+
+};
+
+#endif // TIMEWIDGET_H

+ 81 - 0
common/TimeSelect/timewidget.ui

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TimeWidget</class>
+ <widget class="QFrame" name="TimeWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>190</width>
+    <height>43</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <property name="styleSheet">
+   <string notr="true"/>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout_2">
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>8</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QLabel" name="lbl_tip">
+     <property name="styleSheet">
+      <string notr="true"/>
+     </property>
+     <property name="text">
+      <string>请选择</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDateTimeEdit" name="dateTimeEdit">
+     <property name="styleSheet">
+      <string notr="true"/>
+     </property>
+     <property name="buttonSymbols">
+      <enum>QAbstractSpinBox::NoButtons</enum>
+     </property>
+     <property name="displayFormat">
+      <string>hh:mm:ss</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QPushButton" name="btn_tip">
+     <property name="minimumSize">
+      <size>
+       <width>18</width>
+       <height>18</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>18</width>
+       <height>18</height>
+      </size>
+     </property>
+     <property name="styleSheet">
+      <string notr="true"/>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 2 - 1
common/TipWidget/tipwidget.cpp

@@ -96,7 +96,8 @@ TipWidget::TipWidget(FormType type, QString& text, QWidget *parent) :
     {
         QFontMetrics fm(ui->label_tipTitle->font());
         auto textWidth = fm.width(text);
-        int width = textWidth + this->width() - ui->label_tipTitle->width();
+        // int width = textWidth + this->width() - ui->label_tipTitle->width();
+        int width = textWidth + this->width() - 60;
         this->setFixedWidth(width);
     }
 }

+ 1 - 0
common/TipWidget/tipwidget.h

@@ -21,6 +21,7 @@ public:
     };
 public:
     static const int WIDTH = 168;
+    // static const int WIDTH = 256;
     static const int HEIGHT = 56;
     static void display(FormType type, QWidget* parent = Q_NULLPTR, int nTitleHeight = 48);
     static void display(FormType type, QString text, QWidget* parent = Q_NULLPTR, int nTitleHeight = 48);

+ 101 - 0
common/UIStyle/UIStyleManager.cpp

@@ -0,0 +1,101 @@
+#include "UIStyleManager.h"
+
+#include <QFile>
+#include <QEventLoop>
+#include "LHQLogAPI.h"
+
+UIStyleManager::UIStyleManager()
+{
+    m_qssPath = ":/QSS/QSS";
+}
+
+
+/* 获取样式表路径 */
+QString UIStyleManager::getQSSPath()
+{
+    if(m_UIStyle == enum_UIStyle::UI_Light)
+    {
+        return m_qssPath + m_lightQSS;
+    }
+    else if(m_UIStyle == enum_UIStyle::UI_Dark)
+    {
+        return m_qssPath + m_darkQSS;
+    }
+    return QString();
+}
+
+/* 换肤,修改样式表 */
+void UIStyleManager::setUIStyle(enum_UIStyle style)
+{
+    m_UIStyle = style;
+   
+    /* 打开所有的QSS文件 */
+    readQSSFile();
+
+    /* 发送普通换肤信号信号 */
+    emit signal_qssChanged();
+
+    /* 处理事件,让UI接收到换肤信号 */
+    QEventLoop loop;
+    loop.processEvents();
+
+    LH_WRITE_LOG(QString("换肤成功,皮肤样式:%1").arg((int)m_UIStyle));
+}
+
+/* 读取qss样式文件调用前需要先设置完成样式类型 */
+void UIStyleManager::readQSSFile()
+{
+    QFile file;
+    /* 打开主窗口样式表 */
+    QString qssPath = getQSSPath() + "/transmitterswitch.qss";
+    file.setFileName(qssPath);
+    if(file.open(QIODevice::ReadOnly))
+    {
+        StrQSS_TransmitterSwitch = file.readAll();
+        file.close();
+    }else {
+        LH_WRITE_ERROR(QString("打开样式表失败: %1").arg(qssPath));
+    }
+
+    /* 打开频率按钮的样式表 */
+    qssPath.clear();
+    qssPath = EPUIStyle.getQSSPath() + "/pBtn_frequency.qss";
+    file.setFileName(qssPath);
+    if(file.open(QFile::ReadOnly))
+    {
+        QString qss = file.readAll();
+        StrQSS_PBtnFrequency = qss;
+        file.close();
+    }else {
+        LH_WRITE_ERROR(QString("打开样式表失败: %1").arg(qssPath));
+    }
+
+    /* 打开PlanCard样式 */
+    qssPath.clear();
+    qssPath = EPUIStyle.getQSSPath() + "/plancard.qss";
+    file.setFileName(qssPath);
+    if(file.open(QFile::ReadOnly))
+    {
+        QString qss = file.readAll();
+        StrQSS_PlanCard = qss;
+        file.close();
+    }else {
+        LH_WRITE_ERROR(QString("打开样式表失败: %1").arg(qssPath));
+    }
+
+    /* 打开ManagerPlan样式 */
+    qssPath.clear();
+    qssPath = EPUIStyle.getQSSPath() + "/managerplan.qss";
+    file.setFileName(qssPath);
+    if(file.open(QFile::ReadOnly))
+    {
+        QString qss = file.readAll();
+        StrQSS_ManagerPlan = qss;
+        file.close();
+    }else {
+        LH_WRITE_ERROR(QString("打开样式表失败: %1").arg(qssPath));
+    }
+
+}
+
+

+ 73 - 0
common/UIStyle/UIStyleManager.h

@@ -0,0 +1,73 @@
+#ifndef UISTYLEMANAGER_H
+#define UISTYLEMANAGER_H
+
+
+#include <QObject>
+
+
+
+/**
+ * @brief UI样式
+ * 
+ */
+ enum class enum_UIStyle
+ {
+     UI_Light = 0,               /* 亮色 */
+     UI_Dark = 1                 /* 暗色 */
+ };
+
+
+
+#define EPUIStyle UIStyleManager::getInstance()
+/**
+ * @brief 全局皮肤管理器,大部分的UI从这里加载qss文件
+ * 
+ */
+class UIStyleManager : public QObject
+{
+    Q_OBJECT
+
+    UIStyleManager();
+    UIStyleManager(const UIStyleManager& config) = delete;
+    UIStyleManager& operator=(const UIStyleManager& config) = delete;
+public:
+    ~UIStyleManager() {}
+
+    static UIStyleManager& getInstance()
+    {
+        static UIStyleManager config;
+        return config;
+    }
+
+    QString StrQSS_TransmitterSwitch;       /* 主窗口样式表 */
+    QString StrQSS_PBtnFrequency;           /* 频率按钮样式表 */
+    QString StrQSS_PlanCard;                /* 计划卡片样式表 */
+    QString StrQSS_ManagerPlan;             /* 计划管理窗口样式表 */
+
+    /* 获取样式表路径 */
+    QString getQSSPath();
+    /* 换肤,修改样式表 */
+    void setUIStyle(enum_UIStyle style);
+    /* 获取当前UI风格 */
+    enum_UIStyle getUIStyle() { return m_UIStyle; }
+
+signals:
+    /* 样式表改变了 */
+    void signal_qssChanged();
+
+private:
+    /* 读取qss样式文件 */
+    void readQSSFile();
+
+private:
+    enum_UIStyle m_UIStyle;                 /* 当前UI样式 */
+    QString m_qssPath;                      /* 样式表路径 */
+    const QString m_lightQSS = "/white";    /* 亮色样式表路径 */
+    const QString m_darkQSS = "/dark";      /* 暗色样式表路径 */
+
+    
+};
+
+
+
+#endif /* UISTYLEMANAGER_H */

+ 1 - 1
common/combox/customcombobox.cpp

@@ -17,7 +17,7 @@ CustomComboBox::~CustomComboBox()
 /* 设置下拉框阴影 */
 void CustomComboBox::setViewShadowEffect()
 {
-    /* 设置这个在Linux下,下拉框才会背景透明 */
+    /* 设置这个样式,在Linux下才出现白色背景 */
     setStyle(QStyleFactory::create("Windows"));
     setView(new QListView());
     if (nullptr != view() && nullptr != view()->window()) 

+ 0 - 205
common/warning/warning copy.ui_

@@ -1,205 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Warning</class>
- <widget class="QDialog" name="Warning">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>448</width>
-    <height>249</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Dialog</string>
-  </property>
-  <property name="styleSheet">
-   <string notr="true">QDialog#Warning
-{
-background:transparent;
-}</string>
-  </property>
-  <layout class="QVBoxLayout" name="verticalLayout">
-   <property name="spacing">
-    <number>0</number>
-   </property>
-   <property name="leftMargin">
-    <number>16</number>
-   </property>
-   <property name="topMargin">
-    <number>16</number>
-   </property>
-   <property name="rightMargin">
-    <number>16</number>
-   </property>
-   <property name="bottomMargin">
-    <number>16</number>
-   </property>
-   <item>
-    <widget class="QWidget" name="widget" native="true">
-     <property name="styleSheet">
-      <string notr="true">QWidget{
-background: #FFFFFF;
-border-radius: 8px;
-font-family: 思源黑体R;
-font-weight: 400;
-font-size: 18px;
-color: #3A3F63;
-line-height: 21px;
-text-align: left;
-font-style: normal;
-}
-
-QLabel#label_NC1
-{
-font-family: 思源黑体M;
-font-weight: bold;
-font-size: 18px;
-color: #3A3F63;
-line-height: 27px;
-text-transform: uppercase;
-}
-
-QLabel#label_line
-{
-background: #E6E9F4;
-}
-
-QLabel#label_Warn
-{
-	
-}
-
-QPushButton#pBtn_close{
-	border-image: url(:/ESM-8C_ICON/Dialog_close.png);
-}
-QPushButton#pBtn_close:hover{
-	
-	border-image: url(:/ESM-8C_ICON/Dialog_close2.png);
-}
-
-QPushButton
-{
-	background: #FFFFFF;
-	border: 1px solid #E6E9F4;
-	text-align: center;
-}
-
-
-/********* 普通方框按钮三种状态效果 *********/
-QPushButton#pBtn_cancel
-{
-    background: #FFFFFF;
-    border-radius: 16px;
-    border: 1px solid #E6E9F4;
-    color: #3A3F63;
-}
-QPushButton#pBtn_cancel:hover
-{
-    background: #FFFFFF;
-    border-radius: 16px;
-    border: 1px solid #4458FE;
-    color: #4458FE;
-}
-
-/********* 带有底色按钮三种状态效果 *********/
-QPushButton#pBtn_ok
-{
-    color:white;
-    background: qlineargradient( x0:1,x1:1,y1:0,y2:0,stop:1 #4F8AFF,stop:0 #4B5EFF);
-    border-radius: 16px;
-}
-
-QPushButton#pBtn_ok:hover
-{
-    color:white;
-    background: qlineargradient( x0:1,x1:1,y1:0,y2:0,stop:0 #5D73FF,stop:1 #6092FF);
-    border-radius: 16px;
-}
-</string>
-     </property>
-     <widget class="QLabel" name="label_NC1">
-      <property name="geometry">
-       <rect>
-        <x>32</x>
-        <y>18</y>
-        <width>90</width>
-        <height>18</height>
-       </rect>
-      </property>
-      <property name="text">
-       <string>提示</string>
-      </property>
-     </widget>
-     <widget class="QLabel" name="label_line">
-      <property name="geometry">
-       <rect>
-        <x>0</x>
-        <y>56</y>
-        <width>416</width>
-        <height>1</height>
-       </rect>
-      </property>
-      <property name="text">
-       <string/>
-      </property>
-     </widget>
-     <widget class="QPushButton" name="pBtn_close">
-      <property name="geometry">
-       <rect>
-        <x>368</x>
-        <y>12</y>
-        <width>32</width>
-        <height>32</height>
-       </rect>
-      </property>
-      <property name="text">
-       <string/>
-      </property>
-     </widget>
-     <widget class="QLabel" name="label_Warn">
-      <property name="geometry">
-       <rect>
-        <x>40</x>
-        <y>78</y>
-        <width>331</width>
-        <height>61</height>
-       </rect>
-      </property>
-      <property name="text">
-       <string>删除该设备,“执行计划”中与之对应的计划也会删除!</string>
-      </property>
-     </widget>
-     <widget class="QPushButton" name="pBtn_cancel">
-      <property name="geometry">
-       <rect>
-        <x>248</x>
-        <y>153</y>
-        <width>60</width>
-        <height>32</height>
-       </rect>
-      </property>
-      <property name="text">
-       <string>取消</string>
-      </property>
-     </widget>
-     <widget class="QPushButton" name="pBtn_ok">
-      <property name="geometry">
-       <rect>
-        <x>324</x>
-        <y>153</y>
-        <width>60</width>
-        <height>32</height>
-       </rect>
-      </property>
-      <property name="text">
-       <string>确定</string>
-      </property>
-     </widget>
-    </widget>
-   </item>
-  </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>

+ 100 - 9
common/warning/warning.cpp

@@ -4,10 +4,14 @@
 #include <QPainter>
 #include <QLayout>
 #include <QDebug>
+#include <QFile>
+#include <QStyle>
+#include <QMouseEvent>
 
+#include "OneShadowEffect.h"
+// #include "lhstylemanager.h"
+#include "LHQLogAPI.h"
 
-#include "spdlog/spdlog.h"
-#include "oneshadow.h"
 
 Warning::Warning(QWidget *parent) :
     QDialog(parent),
@@ -18,21 +22,34 @@ Warning::Warning(QWidget *parent) :
     this->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
     this->setAttribute(Qt::WA_TranslucentBackground);
 
+    /* 加载QSS */
+    // QFile file(":/QSS/QSS/warning_light.qss");
+    // if(file.open(QFile::ReadOnly))
+    // {
+    //     QString styleSheet = file.readAll();
+    //     this->setStyleSheet(styleSheet);
+    //     file.close();
+    // }
+
     /* 设置文字自动换行 */
     ui->label_Warn->setWordWrap(true);
     /* 设置文本居中 */
     // ui->label_Warn->setAlignment(Qt::AlignCenter);
     ui->label_Warn->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
     /* 加载警告图标 */
-    ui->label_warnIcon->setStyleSheet(R"(border-image: url(:/ESM-8C_ICON/Tip/Tips2x.png);)");
+    // ui->label_warnIcon->setStyleSheet(R"(border-image: url(:/ESM-8C_ICON/Tip/Tips2x.png);)");
 
     /* 阴影宽度是16 */
     this->layout()->setMargin(SHADOW_W);
-    m_shadow = new OneShadow(QSize(width() - SHADOW_W*2, height() - SHADOW_W*2),SHADOW_W);
+    // m_shadow = new OneShadow(QSize(width() - SHADOW_W*2, height() - SHADOW_W*2),SHADOW_W);
+    auto pShadow = new OneShadowEffect(this);
+    this->setGraphicsEffect(pShadow);
 
     connect(ui->pBtn_close,SIGNAL(clicked()),this,SLOT(close()));
     connect(ui->pBtn_cancel,SIGNAL(clicked()),this,SLOT(close()));
     connect(ui->pBtn_ok,SIGNAL(clicked()),this,SLOT(do_ok()));
+    /* 注册事件过滤器 */
+    ui->pBtn_close->installEventFilter(this);
 }
 
 Warning::~Warning()
@@ -60,12 +77,86 @@ void Warning::setTextWithOneButton(const QString &text)
     moveWarnICON();
 }
 
-void Warning::paintEvent(QPaintEvent *event)
+/* 设置QSS */
+void Warning::setQSS(const QString& qssPath)
+{
+    QString qssFile = qssPath + "/warning.qss";
+    QFile file(qssFile);
+    if(file.open(QIODevice::ReadOnly))
+    {
+        QString stylesheet = file.readAll();
+        this->setStyleSheet(stylesheet);
+        file.close();
+    } else
+    {
+        LH_WRITE_ERROR(QString("打开文件失败:%1").arg(file.fileName()));
+    }
+}
+
+// void Warning::paintEvent(QPaintEvent *event)
+// {
+//     QPainter painter(this);
+//     painter.setRenderHint(QPainter::Antialiasing);
+//     /* 绘制阴影 */
+//     painter.drawImage(QPoint(0,0),m_shadow->image());
+// }
+
+/* 事件过滤器 */
+bool Warning::eventFilter(QObject *watched, QEvent *event)
+{
+    if(watched == ui->pBtn_close)
+    {
+        if(event->type() == QEvent::Enter)
+        {
+            ui->pBtn_close->setProperty("Hover", true);
+            ui->pBtn_close->style()->unpolish(ui->pBtn_close);
+            ui->pBtn_close->style()->polish(ui->pBtn_close);
+            return true;
+        }else if(event->type() == QEvent::Leave)
+        {
+            ui->pBtn_close->setProperty("Hover", false);
+            ui->pBtn_close->style()->unpolish(ui->pBtn_close);
+            ui->pBtn_close->style()->polish(ui->pBtn_close);
+
+            return true;
+        }
+    }
+    return QWidget::eventFilter(watched,event);
+}
+
+
+/* 鼠标点击事件 */
+void Warning::mousePressEvent(QMouseEvent *event)
+{
+    m_lastPos = event->globalPos();
+    event->accept();
+}
+
+/* 鼠标移动事件 */
+void Warning::mouseMoveEvent(QMouseEvent *event)
+{
+    // QRect rect = this->geometry();
+    // rect.setBottom(rect.top()+50);
+    auto point = ui->widget_Top->mapToGlobal(QPoint(0, 0));
+    QRect rect(point, ui->widget_Top->size());
+
+    if(!rect.contains(m_lastPos))
+    {
+        event->accept();
+        return;
+    }
+
+    int dx = event->globalX() - m_lastPos.x();
+    int dy = event->globalY() - m_lastPos.y();
+    move(x()+dx, y()+dy);
+    m_lastPos = event->globalPos();
+    event->accept();
+}
+
+/* 鼠标释放事件 */
+void Warning::mouseReleaseEvent(QMouseEvent *event)
 {
-    QPainter painter(this);
-    painter.setRenderHint(QPainter::Antialiasing);
-    /* 绘制阴影 */
-    painter.drawImage(QPoint(0,0),m_shadow->image());
+    event->accept();
 }
 
 

+ 15 - 2
common/warning/warning.h

@@ -20,11 +20,22 @@ public:
     void setText(const QString& text);
     void setTextWithOneButton(const QString& text);     /* 只有一个确定按钮 */
     bool isOk() const { return m_isOk; }
+    /* 设置QSS */
+    void setQSS(const QString& qssPath);
+
 signals:
     void signal_ok();
 
 protected:
-    void paintEvent(QPaintEvent *event);
+    // void paintEvent(QPaintEvent *event) override;
+    /* 事件过滤器 */
+    bool eventFilter(QObject *watched, QEvent *event) override;
+    /* 鼠标点击事件 */
+    void mousePressEvent(QMouseEvent *event) override;
+    /* 鼠标移动事件 */
+    void mouseMoveEvent(QMouseEvent *event) override;
+    /* 鼠标释放事件 */
+    void mouseReleaseEvent(QMouseEvent *event) override;
 
 private slots:
     void do_ok();
@@ -38,9 +49,11 @@ private:
 private:
     Ui::Warning *ui;
 
-    OneShadow* m_shadow = nullptr;
+    // OneShadow* m_shadow = nullptr;
     const int SHADOW_W = 16;                /* 阴影的大小 */
     bool m_isOk = false;
+
+    QPoint m_lastPos;
 };
 
 #endif // WARNING_H

+ 37 - 122
common/warning/warning.ui

@@ -38,129 +38,8 @@ background:transparent;
    <item>
     <widget class="QWidget" name="widget" native="true">
      <property name="styleSheet">
-      <string notr="true">QWidget{
-background: #FFFFFF;
-border-radius: 8px;
-font-family: 思源黑体R;
-font-weight: 400;
-font-size: 18px;
-color: #3A3F63;
-line-height: 21px;
-text-align: left;
-font-style: normal;
-}
-
-QLabel#label_NC1
-{
-font-family: 思源黑体M;
-font-weight: bold;
-font-size: 18px;
-color: #3A3F63;
-line-height: 27px;
-text-transform: uppercase;
-}
-
-QLabel#label_line
-{
-background: #E6E9F4;
-}
-
-QLabel#label_Warn
-{
-	font-family: 思源黑体R;
-	font-weight: 400;
-	font-size: 18px;
-	color: #3A3F63;
-	line-height: 27px;
-}
-
-QPushButton#pBtn_close{
-	border-image: url(:/ESM-8C_ICON/Dialog_close.png);
-}
-QPushButton#pBtn_close:hover{
-	
-	border-image: url(:/ESM-8C_ICON/Dialog_close2.png);
-}
-
-QPushButton
-{
-	background: #FFFFFF;
-	border: 1px solid #E6E9F4;
-	text-align: center;
-}
-
-
-/********* 普通方框按钮三种状态效果 *********/
-QPushButton#pBtn_cancel
-{
-    background: #FFFFFF;
-    border-radius: 16px;
-    border: 1px solid #E6E9F4;
-    color: #3A3F63;
-}
-QPushButton#pBtn_cancel:hover
-{
-    background: #FFFFFF;
-    border-radius: 16px;
-    border: 1px solid #4458FE;
-    color: #4458FE;
-}
-
-/********* 带有底色按钮三种状态效果 *********/
-QPushButton#pBtn_ok
-{
-    color:white;
-    background: qlineargradient( x0:1,x1:1,y1:0,y2:0,stop:1 #4F8AFF,stop:0 #4B5EFF);
-    border-radius: 16px;
-}
-
-QPushButton#pBtn_ok:hover
-{
-    color:white;
-    background: qlineargradient( x0:1,x1:1,y1:0,y2:0,stop:0 #5D73FF,stop:1 #6092FF);
-    border-radius: 16px;
-}
-</string>
+      <string notr="true"/>
      </property>
-     <widget class="QLabel" name="label_NC1">
-      <property name="geometry">
-       <rect>
-        <x>32</x>
-        <y>18</y>
-        <width>90</width>
-        <height>18</height>
-       </rect>
-      </property>
-      <property name="text">
-       <string>提示</string>
-      </property>
-     </widget>
-     <widget class="QLabel" name="label_line">
-      <property name="geometry">
-       <rect>
-        <x>0</x>
-        <y>56</y>
-        <width>416</width>
-        <height>1</height>
-       </rect>
-      </property>
-      <property name="text">
-       <string/>
-      </property>
-     </widget>
-     <widget class="QPushButton" name="pBtn_close">
-      <property name="geometry">
-       <rect>
-        <x>368</x>
-        <y>12</y>
-        <width>32</width>
-        <height>32</height>
-       </rect>
-      </property>
-      <property name="text">
-       <string/>
-      </property>
-     </widget>
      <widget class="QLabel" name="label_Warn">
       <property name="geometry">
        <rect>
@@ -213,6 +92,42 @@ QPushButton#pBtn_ok:hover
        <string/>
       </property>
      </widget>
+     <widget class="QWidget" name="widget_Top" native="true">
+      <property name="geometry">
+       <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>408</width>
+        <height>56</height>
+       </rect>
+      </property>
+      <widget class="QPushButton" name="pBtn_close">
+       <property name="geometry">
+        <rect>
+         <x>368</x>
+         <y>12</y>
+         <width>32</width>
+         <height>32</height>
+        </rect>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+      <widget class="QLabel" name="label_title">
+       <property name="geometry">
+        <rect>
+         <x>32</x>
+         <y>18</y>
+         <width>90</width>
+         <height>18</height>
+        </rect>
+       </property>
+       <property name="text">
+        <string>提示</string>
+       </property>
+      </widget>
+     </widget>
     </widget>
    </item>
   </layout>

+ 122 - 0
common/warning/warning_dark.qss

@@ -0,0 +1,122 @@
+QWidget
+{
+    background: transparent;
+    /* font-family: 思源黑体R; */
+    /* font-weight: 400; */
+    font-size: 18px;
+    color: #D2D2D2;
+    line-height: 21px;
+    text-align: left;
+    font-style: normal;
+}
+
+QWidget#widget
+{
+    background: #464649;
+    border-radius: 8px 8px 8px 8px;
+}
+
+QWidget#widget_Top
+{
+    background: transparent;
+    border-top-left-radius: 8px;
+    border-top-right-radius: 8px;
+    border-bottom-left-radius: 0px;
+    border-bottom-right-radius: 0px;
+
+    border-bottom: 1px solid rgba(255,255,255,0.15);
+}
+
+QLabel#label_title
+{
+    /* font-family: 思源黑体M; */
+    /* font-weight: normal; */
+    font-size: 18px;
+    color: #D2D2D2;
+    line-height: 27px;
+    text-transform: uppercase;
+}
+
+QLabel#label_line
+{
+    background: #E6E9F4;
+}
+
+/* 设置图标 */
+QLabel#label_warnIcon
+{
+    border-image: url(:/ICON/Tip/Tips2x.png);
+}
+
+QLabel#label_Warn
+{
+	/* font-family: 思源黑体R; */
+	/* font-weight: 400; */
+	font-size: 18px;
+	color: #D2D2D2;
+	line-height: 27px;
+}
+
+/*设置图片居中 */
+QPushButton#pBtn_close
+{
+    background: transparent;
+	/* border-image: url(:/ICON/ICON/Dialog_close.png); */
+    background: transparent;
+    border-radius: 4px;
+    qproperty-icon: url(:/ICON/ICON/Close_Dark.png);
+    qproperty-iconSize: 20px 20px;
+
+    padding-left: 0px;
+}
+QPushButton#pBtn_close[Hover = true]
+{
+    background: transparent;
+	/* border-image: url(:/ICON/ICON/Dialog_close2.png); */
+    background: transparent;
+    border-radius: 4px;
+    qproperty-icon: url(:/ICON/ICON/Close_pass.png);
+    qproperty-iconSize: 20px 20px;
+    padding-left: 0px;
+    border: 1px solid #438EFF;
+}
+
+QPushButton
+{
+	background: #FFFFFF;
+	/* border: 1px solid #E6E9F4; */
+	text-align: center;
+}
+
+
+/********* 普通方框按钮三种状态效果 *********/
+QPushButton#pBtn_cancel:enabled
+{
+    color: #EBEBEB;
+    border: 1px solid rgba(255,255,255,0.25);
+    border-radius: 16px;
+    background: transparent;
+}
+QPushButton#pBtn_cancel:hover
+{
+    color: #EBEBEB;
+    border: 1px solid rgba(255,255,255,0.25);
+    border-radius: 16px;
+    background: rgba(0,0,0,0.15);
+}
+
+
+/********* 带有底色按钮三种状态效果 *********/
+QPushButton#pBtn_ok
+{
+    color:white;
+    background: #438EFF;
+    border-radius: 16px;
+}
+
+QPushButton#pBtn_ok:hover
+{
+    color:white;
+    background: #5F9EFF;
+    border-radius: 16px;
+}

+ 120 - 0
common/warning/warning_light.qss

@@ -0,0 +1,120 @@
+QWidget
+{
+    background: transparent;
+    border-radius: 8px;
+    /* font-family: 思源黑体R; */
+    /* font-weight: 400; */
+    font-size: 18px;
+    color: #3A3F63;
+    line-height: 21px;
+    text-align: left;
+    font-style: normal;
+}
+
+QWidget#widget
+{
+    background: #FFFFFF;
+}
+
+QWidget#widget_Top
+{
+    background: #FFFFFF;
+    border-top-left-radius: 8px;
+    border-top-right-radius: 8px;
+    border-bottom-left-radius: 0px;
+    border-bottom-right-radius: 0px;
+    border-bottom: 1px solid #E6E9F4;
+}
+
+QLabel#label_title
+{
+    /* font-family: 思源黑体M; */
+    /* font-weight: normal; */
+    font-size: 18px;
+    color: #3A3F63;
+    line-height: 27px;
+    text-transform: uppercase;
+}
+
+QLabel#label_line
+{
+    background: #E6E9F4;
+}
+
+/* 设置图标 */
+QLabel#label_warnIcon
+{
+    border-image: url(:/ICON/Tip/Tips2x.png);
+}
+
+QLabel#label_Warn
+{
+	/* font-family: 思源黑体R; */
+	/* font-weight: 400; */
+	font-size: 18px;
+	color: #3A3F63;
+	line-height: 27px;
+}
+
+QPushButton#pBtn_close{
+	/* border-image: url(:/ICON/ICON/Dialog_close.png); */
+    background: transparent;
+    border-radius: 4px;
+    qproperty-icon: url(:/ICON/ICON/Close_Light.png);
+    qproperty-iconSize: 20px 20px;
+    padding-left: 0px;
+    border: 0px solid #E6E9F4;
+}
+QPushButton#pBtn_close[Hover = true]
+{
+	
+	/* border-image: url(:/ICON/ICON/Dialog_close2.png); */
+    background: transparent;
+    border-radius: 4px;
+    qproperty-icon: url(:/ICON/ICON/Close_pass.png);
+    qproperty-iconSize: 20px 20px;
+    padding-left: 0px;
+    border: 1px solid #438EFF;
+}
+
+QPushButton
+{
+	background: #FFFFFF;
+	border: 1px solid #E6E9F4;
+	text-align: center;
+}
+
+
+/********* 普通方框按钮三种状态效果 *********/
+QPushButton#pBtn_cancel
+{
+    background: #FFFFFF;
+    border-radius: 16px;
+    border: 1px solid #E6E9F4;
+    color: #3A3F63;
+}
+QPushButton#pBtn_cancel:hover
+{
+
+    background: #FFFFFF;
+    border-radius: 16px;
+    border: 1px solid #4458FE;
+    color: #4458FE;
+}
+
+/********* 带有底色按钮三种状态效果 *********/
+QPushButton#pBtn_ok
+{
+
+    color:white;
+    background: qlineargradient( x0:1,x1:1,y1:0,y2:0,stop:1 #4F8AFF,stop:0 #4B5EFF);
+    border-radius: 16px;
+}
+
+QPushButton#pBtn_ok:hover
+{
+
+    color:white;
+    background: qlineargradient( x0:1,x1:1,y1:0,y2:0,stop:0 #5D73FF,stop:1 #6092FF);
+    border-radius: 16px;
+}