Sfoglia il codice sorgente

1. 新增启动程序时重置蓝牙服务;2. 新增时分秒等控件

xj 3 mesi fa
parent
commit
cb9e7dafb9

+ 1 - 0
Loto.pro

@@ -14,5 +14,6 @@ CONFIG(debug, debug|release) {
 
 DISTFILES += \
     src/qml/components/MComboBox.qml \
+    src/qml/components/MDateTimePicker.qml \
     src/qml/components/MSelectDateTime.qml \
     src/qml/components/MTimePicker.qml

+ 24 - 24
Loto.pro.user

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE QtCreatorProject>
-<!-- Written by QtCreator 4.14.2, 2026-02-10T09:36:57. -->
+<!-- Written by QtCreator 4.14.2, 2026-02-11T11:47:33. -->
 <qtcreator>
  <data>
   <variable>EnvironmentId</variable>
@@ -8,7 +8,7 @@
  </data>
  <data>
   <variable>ProjectExplorer.Project.ActiveTarget</variable>
-  <value type="int">1</value>
+  <value type="int">0</value>
  </data>
  <data>
   <variable>ProjectExplorer.Project.EditorSettings</variable>
@@ -86,16 +86,16 @@
   <variable>ProjectExplorer.Project.Target.0</variable>
   <valuemap type="QVariantMap">
    <value type="QString" key="DeviceType">GenericLinuxOsType</value>
-   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">DaJiGui</value>
-   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">DaJiGui</value>
-   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{09bab3f2-c9c3-421a-aef1-57a08250e2d5}</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">rk3568</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">rk3568</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{4b7c7020-747d-4c4a-8e7b-c68e8d5127ef}</value>
    <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
    <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
    <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
     <value type="int" key="EnableQmlDebugging">0</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Debug</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Debug</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Debug</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Debug</value>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
      <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
       <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
@@ -134,8 +134,8 @@
     <value type="int" key="RunSystemFunction">0</value>
    </valuemap>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Release</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Release</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Release</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Release</value>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
      <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
       <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
@@ -176,8 +176,8 @@
    </valuemap>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
     <value type="int" key="EnableQmlDebugging">0</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Profile</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Profile</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Profile</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Profile</value>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
      <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
       <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
@@ -327,10 +327,10 @@
     <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes">
      <value type="QString">LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH</value>
      <value type="QString">QML2_IMPORT_PATH=$QTDIR/qml:$QML2_IMPORT_PATH</value>
-     <value type="QString">QTDIR=/home/teamhd/qt5</value>
+     <value type="QString">QTDIR=/opt/qt5</value>
      <value type="QString">QT_PLUGIN_PATH=$QTDIR/plugins:$QT_PLUGIN_PATH</value>
     </valuelist>
-    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">src (on DaJiGui)</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">src (on rk3568)</value>
     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">RemoteLinuxRunConfiguration:/home/kim/Desktop/ISCS_LOTO_Linux/src/src.pro</value>
     <value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/kim/Desktop/ISCS_LOTO_Linux/src/src.pro</value>
     <value type="int" key="RemoteLinux.EnvironmentAspect.Version">1</value>
@@ -347,16 +347,16 @@
   <variable>ProjectExplorer.Project.Target.1</variable>
   <valuemap type="QVariantMap">
    <value type="QString" key="DeviceType">GenericLinuxOsType</value>
-   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">rk3568</value>
-   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">rk3568</value>
-   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{4b7c7020-747d-4c4a-8e7b-c68e8d5127ef}</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">DaJiGui</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">DaJiGui</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{09bab3f2-c9c3-421a-aef1-57a08250e2d5}</value>
    <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
    <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
    <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
     <value type="int" key="EnableQmlDebugging">0</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Debug</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Debug</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Debug</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Debug</value>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
      <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
       <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
@@ -395,8 +395,8 @@
     <value type="int" key="RunSystemFunction">0</value>
    </valuemap>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Release</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Release</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Release</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Release</value>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
      <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
       <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
@@ -437,8 +437,8 @@
    </valuemap>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
     <value type="int" key="EnableQmlDebugging">0</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Profile</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-rk3568-Profile</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Profile</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/kim/Desktop/ISCS_LOTO_Linux/build-Loto-DaJiGui-Profile</value>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
      <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
       <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
@@ -588,10 +588,10 @@
     <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes">
      <value type="QString">LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH</value>
      <value type="QString">QML2_IMPORT_PATH=$QTDIR/qml:$QML2_IMPORT_PATH</value>
-     <value type="QString">QTDIR=/opt/qt5</value>
+     <value type="QString">QTDIR=/home/teamhd/qt5</value>
      <value type="QString">QT_PLUGIN_PATH=$QTDIR/plugins:$QT_PLUGIN_PATH</value>
     </valuelist>
-    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">src (on rk3568)</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">src (on DaJiGui)</value>
     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">RemoteLinuxRunConfiguration:/home/kim/Desktop/ISCS_LOTO_Linux/src/src.pro</value>
     <value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/kim/Desktop/ISCS_LOTO_Linux/src/src.pro</value>
     <value type="int" key="RemoteLinux.EnvironmentAspect.Version">1</value>

+ 1 - 0
src/qml.qrc

@@ -41,5 +41,6 @@
         <file>qml/components/MComboBox.qml</file>
         <file>qml/components/MSelectDateTime.qml</file>
         <file>qml/components/MTimePicker.qml</file>
+        <file>qml/components/MDateTimePicker.qml</file>
     </qresource>
 </RCC>

+ 14 - 0
src/qml/components/FormCard.qml

@@ -278,7 +278,21 @@ Rectangle {
                         formControlRow.item.signalTextChanged.connect(control.slotCollectInputInfo);
                         control.signalSubmit.connect(formControlRow.item.slotShowRequiredMsg);
                     } else if (type === "datetime") {
+                        formControlRow.source = "MDateTimePicker.qml";
+                        formControlRow.item.controlId = id;
+                        formControlRow.item.modelIndex = groupIndex;
+                        formControlRow.item.placeholderText = placeholder;
+                        formControlRow.item.required = required;
+                        formControlRow.item.requiredMsg = requiredMessage !== "" ? requiredMessage : label.trim()+"不能为空";
+                        formControlRow.item.height = 48;
+                        formControlRow.item.enabled = !control.readOnly;
+                        // 回显值
+                        if (value !== undefined && value !== "") {
+                            formControlRow.item.text = value;
+                        }
 
+                        formControlRow.item.signalTextChanged.connect(control.slotCollectInputInfo);
+                        control.signalSubmit.connect(formControlRow.item.slotShowRequiredMsg);
                     } else if (type === "timepicker") {
                         formControlRow.source = "MTimePicker.qml";
                         formControlRow.item.controlId = id;

+ 174 - 0
src/qml/components/MDateTimePicker.qml

@@ -0,0 +1,174 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+
+Item {
+    id: control
+    width: 240
+    height: 40
+
+    // === 接口属性 & 信号 ===
+    property string controlId
+    property int modelIndex         // 特殊标记,可以省略循环WorkNodeFormModel的时间
+    property alias text: textField.text
+    property alias placeholderText: textField.placeholderText
+    signal accepted()  // 输入回车触发
+    signal signalTextChanged(int index, string id, string data);
+
+    property bool required: false
+    property string requiredMsg
+    property bool showRequiredMsg: false
+
+    // === 样式属性 ===
+    property int fontSize: 16
+    property real radius: 10
+    property string showDatePopupSymbo: "\uf073"
+    property color textColor: "white"
+
+    // === 状态属性 ===
+    property bool enabled: true
+    property bool backgroundVisible: true  // 背景显示控制
+
+    // === 背景与阴影 ===
+//    MultiEffect {
+//        source: background
+//        anchors.fill: background
+//        shadowEnabled: true
+//        shadowColor: theme.shadowColor
+//        shadowBlur: theme.shadowBlur
+//        shadowHorizontalOffset: theme.shadowXOffset
+//        shadowVerticalOffset: theme.shadowYOffset
+//    }
+
+    Rectangle {
+        id: background
+        anchors.fill: parent
+        radius: control.radius
+        // 无背景时:选中用主题高亮色,未选中用次级色;有背景时沿用主题边框色
+        // border.color: control.backgroundVisible
+        //                ? theme.getBorderColor(textField.activeFocus)
+        //                : (textField.activeFocus ? theme.focusColor : "#40A9FF")
+        border.color: "#40A9FF"
+        border.width: 1
+        // color: control.backgroundVisible ? theme.secondaryColor : "transparent"
+        color: "transparent"
+        opacity: control.enabled ? 1.0 : 0.6
+    }
+
+    // === 内容布局 ===
+    RowLayout {
+        id: layout
+        anchors.fill: parent
+        // anchors.margins: 8
+        spacing: 6
+
+        // === 输入框主体 ===
+        TextField {
+            id: textField
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+            Layout.alignment: Qt.AlignVCenter
+
+            font.pixelSize: control.fontSize
+            color: control.textColor
+            placeholderTextColor: control.textColor
+            enabled: control.enabled
+            verticalAlignment: Text.AlignVCenter
+            echoMode: TextInput.Normal
+            background: null
+            onAccepted: control.accepted()
+
+            onFocusChanged: {
+                if (!focus) {
+                    forceActiveFocus()
+                }
+            }
+
+            onTextChanged: signalTextChanged(control.modelIndex, control.controlId, textField.text);
+        }
+
+        Text {
+            id: popupSymbo
+            Layout.rightMargin: 8
+            text: showDatePopupSymbo
+            color: "#666"
+            font.pixelSize: 16
+            verticalAlignment: Text.AlignVCenter
+            horizontalAlignment: Text.AlignHCenter
+
+            MouseArea {
+                anchors.fill: parent
+                enabled: control.enabled
+                cursorShape: Qt.PointingHandCursor
+                onClicked: {
+                    // TODO: 显示Calendar控件
+                    // console.log("显示Calendar控件");
+                    calendarPopup.open();
+                }
+            }
+        }
+    }
+    Popup {
+        id: calendarPopup
+        x: layout.width - width
+        y: -__calendar.height/2 - textField.height - 10
+        width: 200
+        height: 100
+        modal: true  // 设置是否为模态窗口
+        // focus: visible  // 获取焦点
+        closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside  // 设置关闭策略
+
+        Row {
+            anchors.centerIn: parent
+
+            MCalendar {
+                id: __calendar
+            }
+            MSelectDateTime {
+                id: __selectTime
+            }
+        }
+
+        Connections {
+            target: __calendar
+            function onDateClicked(clickedDate) {
+                var hour = padZero(__selectTime.selectedHour);
+                var minute = padZero(__selectTime.selectedMinute);
+                var second = padZero(__selectTime.selectedSecond);
+                textField.text = Qt.formatDate(clickedDate, "yyyy-MM-dd") + " " + hour + ":" + minute + ":" + second;
+            }
+        }
+
+        Connections {
+            target: __selectTime
+            function onTimeChanged(hour, minute, second) {
+                textField.text = Qt.formatDate(__calendar.selectedDate, "yyyy-MM-dd") + " " + hour + ":" + minute + ":" + second;
+            }
+        }
+    }
+
+    Text {
+        id: textRequiredMsg
+        anchors.top: layout.bottom
+        anchors.topMargin: 15
+        anchors.left: layout.left
+        visible: control.required && control.showRequiredMsg
+        text: qsTr(requiredMsg)
+        color: "red"
+        font.pixelSize: control.fontSize
+        // 普通文字不使用图标字体
+        font.bold: true
+        verticalAlignment: Text.AlignVCenter
+    }
+
+    function slotShowRequiredMsg(submit) {
+        if (submit && textField.text === "") {
+            control.showRequiredMsg = true;
+        }
+    }
+
+    function padZero(num) {
+        const n = Number(num) || 0;
+        return n < 10 ? "0" + n : n.toString();
+    }
+}

+ 88 - 105
src/qml/components/MSelectDateTime.qml

@@ -4,32 +4,26 @@ import QtQuick.Layouts 1.12
 Item {
     id: control
 
-    // ==== 外部接口====
+    // ==== 外部接口:拆分独立属性(核心修复:触发属性更新)====
     property bool backgroundVisible: true
     property real radius: 20
     property int padding: 15
     property bool shadowEnabled: true
 
-    // 选中的时分秒
-    property var selectedTime: {
-        const now = new Date();
-        return {
-            hour: now.getHours() || 0,
-            minute: now.getMinutes() || 0,
-            second: now.getSeconds() || 0
-        };
-    }
-    signal timeChanged(var newTime)  // 时间变更信号
+    property int selectedHour: new Date().getHours() || 0
+    property int selectedMinute: new Date().getMinutes() || 0
+    property int selectedSecond: new Date().getSeconds() || 0
+    signal timeChanged(string hour, string minute, string second)  // 调整信号参数
 
     // 滑动滚轮配置
     property int wheelItemHeight: 40  // 每个数值项高度
-    property int wheelVisibleCount: 5 // 滚轮可见项数(奇数,确保中间项居中
+    property int wheelVisibleCount: 5 // 滚轮可见项数(奇数)
     property real wheelWidth: 80      // 单个滚轮宽度
 
     property real wheelVisibleHeight: wheelItemHeight * wheelVisibleCount
     property real centerOffset: wheelVisibleHeight / 2
     implicitWidth: wheelWidth * 3 + padding * 2 + 20
-    implicitHeight: wheelVisibleHeight + padding * 2 + 20 // 预留标签高度
+    implicitHeight: wheelVisibleHeight + padding * 2 + 20
 
     function padZero(num) {
         const n = Number(num) || 0;
@@ -38,33 +32,44 @@ Item {
 
     function calculateContentY(selectedIndex, totalCount) {
         let contentY = (selectedIndex * wheelItemHeight) + (wheelItemHeight/2) - control.centerOffset;
-        // 最大值居中的临界值
-        const maxContentY = ((totalCount - 1) * wheelItemHeight) + (wheelItemHeight/2) - control.centerOffset;
-        // 边界校验:确保contentY在合法范围
+        const maxContentY = ((totalCount - 1) * wheelItemHeight) - (control.centerOffset - wheelItemHeight/2);
         contentY = Math.max(0, Math.min(contentY, maxContentY));
         return contentY;
     }
 
-    function snapToItem(flickable, totalCount) {
-        const currentIndex = Math.round((flickable.contentY + control.centerOffset) / wheelItemHeight);
-        let targetIndex = (currentIndex % totalCount + totalCount) % totalCount;
-        // 计算精准的居中位置
-        const targetContentY = calculateContentY(targetIndex, totalCount);
+    // 滑动/拖拽结束后
+    function snapToItem(flickable, totalCount, targetProp) {
+        const rawIndex = (flickable.contentY + control.centerOffset - wheelItemHeight/2) / wheelItemHeight;
+        let targetIndex = Math.round(rawIndex);
+        targetIndex = Math.max(0, Math.min(targetIndex, totalCount - 1));
 
-        flickable.contentY = Qt.binding(function() {
-            return targetContentY;
-        });
-        flickable.flickDeceleration = 2000; // 优化滑动手感
-
-        if (flickable === hourWheel) {
-            control.selectedTime.hour = targetIndex;
-        } else if (flickable === minuteWheel) {
-            control.selectedTime.minute = targetIndex;
-        } else if (flickable === secondWheel) {
-            control.selectedTime.second = targetIndex;
+        const targetContentY = calculateContentY(targetIndex, totalCount);
+        flickable.contentY = targetContentY;
+        flickable.flickDeceleration = 2000;
+
+        if (targetProp === "hour") {
+            control.selectedHour = targetIndex;
+        } else if (targetProp === "minute") {
+            control.selectedMinute = targetIndex;
+        } else if (targetProp === "second") {
+            control.selectedSecond = targetIndex;
         }
         // 触发时间变更信号
-        control.timeChanged(control.selectedTime);
+        control.timeChanged(padZero(control.selectedHour), padZero(control.selectedMinute), padZero(control.selectedSecond));
+    }
+
+    function updateRealTimeValue(flickable, totalCount, targetProp) {
+        const rawIndex = (flickable.contentY + control.centerOffset - wheelItemHeight/2) / wheelItemHeight;
+        let targetIndex = Math.round(rawIndex);
+        targetIndex = Math.max(0, Math.min(targetIndex, totalCount - 1));
+
+        if (targetProp === "hour") {
+            control.selectedHour = targetIndex;
+        } else if (targetProp === "minute") {
+            control.selectedMinute = targetIndex;
+        } else if (targetProp === "second") {
+            control.selectedSecond = targetIndex;
+        }
     }
 
     // ==== 视觉背景====
@@ -77,20 +82,7 @@ Item {
         layer.enabled: control.shadowEnabled && control.backgroundVisible
     }
 
-    // ==== 中间选中线 ====
-    Rectangle {
-        id: centerLine
-        anchors.verticalCenter: parent.verticalCenter
-        x: padding + 10
-        width: wheelWidth * 3 + 20
-        height: 2
-        color: "transparent"
-        border.width: 1
-        border.color: "#5a8fc4"
-        z: 10
-    }
-
-    // ==== 主体布局:时/分/秒滚轮并排 ====
+    // ==== 主体布局:时/分/秒滚轮 ====
     RowLayout {
         id: wheelsContainer
         anchors.centerIn: parent
@@ -98,7 +90,7 @@ Item {
         spacing: 10
         width: wheelWidth * 3 + 20
 
-        // ==== 小时滚轮(含顶部标签)====
+        // ==== 小时滚轮 ====
         ColumnLayout {
             spacing: 2
 
@@ -107,29 +99,25 @@ Item {
                 Layout.alignment: Qt.AlignHCenter
                 text: "时"
                 font.pixelSize: 14
-                color: "#000000" // 标签固定黑色
+                color: "#000000"
                 font.bold: true
             }
 
             Flickable {
                 id: hourWheel
                 width: wheelWidth
-                height: wheelVisibleHeight // 改用计算后的可见高度
+                height: wheelVisibleHeight
                 clip: true
                 flickableDirection: Flickable.VerticalFlick
-                boundsBehavior: Flickable.StopAtBounds // 防止滚动越界
+
+                boundsBehavior: Flickable.DragOverBounds
                 contentWidth: wheelWidth
-                contentHeight: 24 * wheelItemHeight
-                contentY: calculateContentY(control.selectedTime.hour, 24)
-
-                // 滑动结束后平滑对齐
-                onFlickEnded: snapToItem(hourWheel, 24)
-                onContentYChanged: {
-                    const previewIndex = Math.round((contentY + control.centerOffset) / wheelItemHeight);
-                    const safeIndex = (previewIndex % 24 + 24) % 24;
-                    console.log(padZero(safeIndex), safeIndex)
-                    console.log(control.selectedTime.hour, control.selectedTime.hour === 11);
-                }
+                contentHeight: 24 * wheelItemHeight + wheelVisibleHeight/2
+                contentY: calculateContentY(control.selectedHour, 24)
+
+                onFlickEnded: snapToItem(hourWheel, 24, "hour")
+                onDragEnded: snapToItem(hourWheel, 24, "hour")
+                onContentYChanged: updateRealTimeValue(hourWheel, 24, "hour")
 
                 Column {
                     id: hourColumn
@@ -142,19 +130,20 @@ Item {
                             height: wheelItemHeight
                             text: padZero(modelData)
                             font.pixelSize: 18
-                            font.bold: modelData === control.selectedTime.hour
-                            color: modelData === control.selectedTime.hour
-                                ? "#5a8fc4"
-                                : "#000000"
+
+                            font.bold: modelData === control.selectedHour
+                            color: modelData === control.selectedHour ? "#5a8fc4" : "#000000"
                             horizontalAlignment: Text.AlignHCenter
                             verticalAlignment: Text.AlignVCenter
+                            // 调试:打印当前值和选中值
+//                            Component.onCompleted: console.log("Hour delegate:", modelData)
                         }
                     }
                 }
             }
         }
 
-        // ==== 分钟滚轮(含顶部标签)====
+        // ==== 分钟滚轮 ====
         ColumnLayout {
             spacing: 2
 
@@ -163,7 +152,7 @@ Item {
                 Layout.alignment: Qt.AlignHCenter
                 text: "分"
                 font.pixelSize: 14
-                color: "#000000" // 标签固定黑色
+                color: "#000000"
                 font.bold: true
             }
 
@@ -173,16 +162,14 @@ Item {
                 height: wheelVisibleHeight
                 clip: true
                 flickableDirection: Flickable.VerticalFlick
-                boundsBehavior: Flickable.StopAtBounds
+                boundsBehavior: Flickable.DragOverBounds
                 contentWidth: wheelWidth
-                contentHeight: 60 * wheelItemHeight
-                contentY: calculateContentY(control.selectedTime.minute, 60)
+                contentHeight: 60 * wheelItemHeight + wheelVisibleHeight/2
+                contentY: calculateContentY(control.selectedMinute, 60)
 
-                onFlickEnded: snapToItem(minuteWheel, 60)
-                onContentYChanged: {
-                    const previewIndex = Math.round((contentY + control.centerOffset) / wheelItemHeight);
-                    const safeIndex = (previewIndex % 60 + 60) % 60;
-                }
+                onFlickEnded: snapToItem(minuteWheel, 60, "minute")
+                onDragEnded: snapToItem(minuteWheel, 60, "minute")
+                onContentYChanged: updateRealTimeValue(minuteWheel, 60, "minute")
 
                 Column {
                     id: minuteColumn
@@ -195,10 +182,8 @@ Item {
                             height: wheelItemHeight
                             text: padZero(modelData)
                             font.pixelSize: 18
-                            font.bold: modelData === control.selectedTime.minute
-                            color: modelData === control.selectedTime.minute
-                                ? "#5a8fc4"
-                                : "#000000"
+                            font.bold: modelData === control.selectedMinute
+                            color: modelData === control.selectedMinute ? "#5a8fc4" : "#000000"
                             horizontalAlignment: Text.AlignHCenter
                             verticalAlignment: Text.AlignVCenter
                         }
@@ -216,7 +201,7 @@ Item {
                 Layout.alignment: Qt.AlignHCenter
                 text: "秒"
                 font.pixelSize: 14
-                color: "#000000" // 标签固定黑色
+                color: "#000000"
                 font.bold: true
             }
 
@@ -226,16 +211,14 @@ Item {
                 height: wheelVisibleHeight
                 clip: true
                 flickableDirection: Flickable.VerticalFlick
-                boundsBehavior: Flickable.StopAtBounds
+                boundsBehavior: Flickable.DragOverBounds
                 contentWidth: wheelWidth
-                contentHeight: 60 * wheelItemHeight
-                contentY: calculateContentY(control.selectedTime.second, 60)
+                contentHeight: 60 * wheelItemHeight + wheelVisibleHeight/2
+                contentY: calculateContentY(control.selectedSecond, 60)
 
-                onFlickEnded: snapToItem(secondWheel, 60)
-                onContentYChanged: {
-                    const previewIndex = Math.round((contentY + control.centerOffset) / wheelItemHeight);
-                    const safeIndex = (previewIndex % 60 + 60) % 60;
-                }
+                onFlickEnded: snapToItem(secondWheel, 60, "second")
+                onDragEnded: snapToItem(secondWheel, 60, "second")
+                onContentYChanged: updateRealTimeValue(secondWheel, 60, "second")
 
                 Column {
                     id: secondColumn
@@ -248,10 +231,8 @@ Item {
                             height: wheelItemHeight
                             text: padZero(modelData)
                             font.pixelSize: 18
-                            font.bold: modelData === control.selectedTime.second
-                            color: modelData === control.selectedTime.second
-                                ? "#5a8fc4"
-                                : "#000000"
+                            font.bold: modelData === control.selectedSecond
+                            color: modelData === control.selectedSecond ? "#5a8fc4" : "#000000"
                             horizontalAlignment: Text.AlignHCenter
                             verticalAlignment: Text.AlignVCenter
                         }
@@ -261,21 +242,23 @@ Item {
         }
     }
 
-    // ==== 生命周期:初始化居中 ====
+    // ==== 初始化 ====
     Component.onCompleted: {
         Qt.callLater(() => {
-            hourWheel.contentY = calculateContentY(control.selectedTime.hour, 24);
-            minuteWheel.contentY = calculateContentY(control.selectedTime.minute, 60);
-            secondWheel.contentY = calculateContentY(control.selectedTime.second, 60);
+            hourWheel.contentY = calculateContentY(control.selectedHour, 24);
+            minuteWheel.contentY = calculateContentY(control.selectedMinute, 60);
+            secondWheel.contentY = calculateContentY(control.selectedSecond, 60);
         });
     }
 
-    // ==== 监听外部修改selectedTime,同步更新滚轮位置 ====
-    onSelectedTimeChanged: {
-        Qt.callLater(() => {
-            hourWheel.contentY = calculateContentY(Number(control.selectedTime.hour) || 0, 24);
-            minuteWheel.contentY = calculateContentY(Number(control.selectedTime.minute) || 0, 60);
-            secondWheel.contentY = calculateContentY(Number(control.selectedTime.second) || 0, 60);
-        });
-    }
+    // ==== 监听外部修改(独立属性)====
+    onSelectedHourChanged: Qt.callLater(() => {
+        hourWheel.contentY = calculateContentY(control.selectedHour, 24);
+    })
+    onSelectedMinuteChanged: Qt.callLater(() => {
+        minuteWheel.contentY = calculateContentY(control.selectedMinute, 60);
+    })
+    onSelectedSecondChanged: Qt.callLater(() => {
+        secondWheel.contentY = calculateContentY(control.selectedSecond, 60);
+    })
 }

+ 2 - 2
src/qml/components/MTimePicker.qml

@@ -125,8 +125,8 @@ Item {
 
         Connections {
             target: __calendar
-            function onTimeChanged(clickedDate) {
-                textField.text = Qt.formatDate(clickedDate, "HH-mm-SS");
+            function onTimeChanged(hour, minute, second) {
+                textField.text = hour + ":" + minute + ":" + second;
             }
         }
     }

+ 2 - 2
src/qml/main.qml

@@ -13,8 +13,8 @@ ApplicationWindow {
     width: 1920
     height: 1080
 
-    //visibility: Window.FullScreen       // 设置窗口为全屏
-    visibility: Window.Windowed          // 窗口模式,非全屏
+    visibility: Window.FullScreen       // 设置窗口为全屏
+    //visibility: Window.Windowed          // 窗口模式,非全屏
     flags: Qt.FramelessWindowHint       // 设置窗口无边框
 
     property int defaultLogoutSeconds: 120 + 1

+ 81 - 7
src/usr/BluetoothClient.cpp

@@ -6,15 +6,89 @@
 #include <QProcess>
 #include <QDBusPendingReply>
 
+bool executeSystemCommand(const QString& cmd, int waitMs = 0) {
+    QProcess process;
+    QStringList cmdParts = cmd.split(" ", Qt::SkipEmptyParts);
+    if (cmdParts.isEmpty()) {
+        qWarning() << "[BLE] 空命令,执行失败!";
+        return false;
+    }
+    QString command = cmdParts.takeFirst();
+    QStringList arguments = cmdParts;
+
+    qDebug() << "[BLE] 执行命令:" << command << arguments;
+    process.start(command, arguments);
+
+    // 等待命令执行完成(超时3秒)
+    bool finished = process.waitForFinished(3000);
+    if (!finished) {
+        qWarning() << "[BLE] 命令执行超时:" << cmd;
+        process.kill();
+        return false;
+    }
+
+    // 检查执行结果
+    if (process.exitCode() != 0) {
+        qWarning() << "[BLE] 命令执行失败(码:" << process.exitCode() << "):" << cmd
+                   << "\n错误输出:" << QString(process.readAllStandardError()).trimmed();
+        return false;
+    }
+
+    // 等待指定时间(状态生效)
+    if (waitMs > 0) {
+        QThread::msleep(waitMs);
+    }
+    return true;
+}
+
 void releaseBluetoothResource() {
-    qDebug() << "[BLE] 释放系统蓝牙资源...";
-    system("sudo killall bluetoothctl 2>/dev/null");
-    system("sudo systemctl stop bluetooth");
-    system("sudo hciconfig hci0 down");
+    qDebug() << "[BLE] 开始模拟手动开关蓝牙,重置蓝牙状态...";
+
+    // ========== 步骤1:彻底禁用蓝牙(模拟turn bluetooth off) ==========
+    qDebug() << "[BLE] 1. 阻塞蓝牙rfkill软开关";
+    executeSystemCommand("rfkill block bluetooth", 500);
+
+    qDebug() << "[BLE] 2. 停止bluetooth服务";
+    executeSystemCommand("systemctl stop bluetooth", 1000);
+
+    qDebug() << "[BLE] 3. 关闭hci0硬件接口";
+    executeSystemCommand("hciconfig hci0 down", 1000);
+
+    qDebug() << "[BLE] 4. 杀死所有蓝牙相关进程(确保彻底关闭)";
+    executeSystemCommand("killall bluetoothd bluetoothctl bluealsa 2>/dev/null", 1000);
+
+    // ========== 步骤2:等待蓝牙彻底关闭 ==========
     QThread::msleep(2000);
-    system("sudo hciconfig hci0 up");
-    system("sudo systemctl start bluetooth");
-    QThread::msleep(1000); // 等待服务启动
+
+    // ========== 步骤3:重新启用蓝牙(模拟turn bluetooth on) ==========
+    qDebug() << "[BLE] 5. 解除蓝牙rfkill软阻塞(关键:手动开关的核心)";
+    executeSystemCommand("rfkill unblock bluetooth", 500);
+
+    qDebug() << "[BLE] 6. 启动hci0硬件接口";
+    executeSystemCommand("hciconfig hci0 up", 1000);
+
+    qDebug() << "[BLE] 7. 重启bluetooth服务(强制重启)";
+    executeSystemCommand("systemctl restart bluetooth", 1500);
+
+    qDebug() << "[BLE] 8. 启用蓝牙适配器(DBus指令,模拟桌面操作)";
+    // 通过DBus向bluez发送启用指令(核心:和手动点击开关等效)
+    executeSystemCommand(
+        "dbus-send --system --dest=org.bluez /org/bluez/hci0 "
+        "org.freedesktop.DBus.Properties.Set string:org.bluez.Adapter1 "
+        "string:Powered variant:boolean:true",
+        1000
+    );
+
+    // ========== 步骤4:等待蓝牙初始化完成(关键:BLE搜索需要时间) ==========
+    qDebug() << "[BLE] 9. 等待蓝牙服务完全初始化...";
+    QThread::msleep(3000);
+
+    // 验证蓝牙状态
+    qDebug() << "[BLE] 蓝牙重置完成,当前状态:";
+    executeSystemCommand("hciconfig hci0");
+    executeSystemCommand("rfkill list bluetooth");
+
+    qDebug() << "[BLE] 蓝牙资源释放/重置完成!";
 }
 
 BLEClient::BLEClient(QObject *parent) : QThread(parent)

+ 7 - 7
src/usr/CANClient.cpp

@@ -470,13 +470,13 @@ void CANClient::slotUpdateLockedStatus(quint8 nodeId, const CANLocksControl &sta
                 }
             }
         }
-        else if ((lockHasKeyMap.find(lockNum) != lockHasKeyMap.end() && !lockHasKeyMap[lockNum]) &&
-                 (lockLockedMap.find(lockNum) != lockLockedMap.end() && lockLockedMap[lockNum])) {
-            writeStatus.lockNums.append(lockNum);
-            writeStatus.lockLockedMap.insert(lockNum, false);
-            writeStatus.lockWorkMap.insert(lockNum, true);
-            isWrite = true;
-        }
+//        else if ((lockHasKeyMap.find(lockNum) != lockHasKeyMap.end() && !lockHasKeyMap[lockNum]) &&
+//                 (lockLockedMap.find(lockNum) != lockLockedMap.end() && lockLockedMap[lockNum])) {
+//            writeStatus.lockNums.append(lockNum);
+//            writeStatus.lockLockedMap.insert(lockNum, false);
+//            writeStatus.lockWorkMap.insert(lockNum, true);
+//            isWrite = true;
+//        }
 //        if ((lockHasKeyMap.find(lockNum) != lockHasKeyMap.end() && !lockHasKeyMap[lockNum]) &&
 //                 (lockLockedMap.find(lockNum) != lockLockedMap.end() && !lockLockedMap[lockNum])) {
 //            writeStatus.lockNums.append(lockNum);