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

feat: 完成版本v1.15.24迭代更新

1. 新增设备位置选择页面及相关定位功能
2. 新增work_sta_bzy2设备状态字典项
3. 优化bzy设备详情页,新增静态数据展示面板
4. 重构滑块组件逻辑,优化交互体验
5. 调整设备列表跳转逻辑及样式适配
6. 修复部分页面数据展示异常问题
7. 优化设备控制页面功能与布局
allen 3 днів тому
батько
коміт
9fdaec03ef

+ 2 - 2
manifest.json

@@ -2,8 +2,8 @@
     "name" : "云飞智控",
     "appid" : "__UNI__DBA6730",
     "description" : "",
-    "versionName" : "1.15.23",
-    "versionCode" : 11523,
+    "versionName" : "1.15.24",
+    "versionCode" : 11524,
     "transformPx" : false,
     "sassImplementationName" : "node-sass",
     /* 5+App特有相关 */

+ 8 - 0
pages.json

@@ -271,6 +271,14 @@
       }
     },
     {
+      "path": "pages/equipList2/seabox/locationPicker",
+      "style": {
+        "navigationBarTitleText": "选择地址",
+        "enablePullDownRefresh": false,
+				"navigationStyle": "custom"
+      }
+    },
+    {
       "path": "pages/distribution/index",
       "style": {
         "navigationBarTitleText": "设备分布",

+ 81 - 27
pages/bzy/components/deviceData.vue

@@ -22,7 +22,7 @@
 
       <!-- 主要数据面板 -->
       <view class="main-data-panel">
-        <view class="data-column-left">
+        <view class="data-column-left" v-if="!isStatic">
           <view class="data-item">
             <text class="data-value">{{ objItem.ds == '1' ? '开机' : '关机' }}</text>
             <text class="data-label">设备开机</text>
@@ -36,6 +36,40 @@
             <text class="data-label">设备版本</text>
           </view>
         </view>
+        <view class="data-column-left" v-else>
+          <view class="data-item">
+            <text class="data-value">{{ staticObj.vbat }}</text>
+            <text class="data-label">电量</text>
+          </view>
+          <view class="data-item">
+            <text class="data-value">{{ parseInt(staticObj.tps) == 1 ? '温控保护' : this.formartWorkSta(staticObj.work_sta) }}</text>
+            <text class="data-label">工作状态</text>
+          </view>
+          <view class="data-item">
+            <text class="data-value">{{ staticObj.at }}℃</text>
+            <text class="data-label">环境温度</text>
+          </view>
+          <view class="data-item">
+            <text class="data-value">{{ staticObj.csq }}</text>
+            <text class="data-label">信号强度</text>
+          </view>
+          <view class="data-item">
+            <text class="data-value">{{ staticObj.ds == 0 ? '开机' : '关机' }}</text>
+            <text class="data-label">设备状态</text>
+          </view>
+          <view class="data-item">
+            <text class="data-value">{{ staticObj.tps == 1 ? '温控保护' : '正常' }}</text>
+            <text class="data-label">温控状态</text>
+          </view>
+          <view class="data-item">
+            <text class="data-value">{{ staticObj.ah }}%RH</text>
+            <text class="data-label">环境湿度</text>
+          </view>
+          <view class="data-item">
+            <text class="data-value">{{ staticObj.dver }}</text>
+            <text class="data-label">设备版本</text>
+          </view>
+        </view>
         <!-- <view class="data-column-right">
           <view class="device-image-container">
             <image class="device-image" :src="formatDevImg" mode="aspectFit"></image>
@@ -62,7 +96,7 @@
         </view> -->
       </view>
       <view class="chart-content">
-        <view class="chart-canvas-container" v-if="!show" :key="chartKey">
+        <view class="chart-canvas-container" v-show="!show" :key="chartKey">
           <!-- <div id="tempChart" class="chart-canvas"></div> -->
 		      <qiun-data-charts type="line" :chartData="chartData" :canvas2d="true" :inScrollView="true" :opts="opts" :ontouch="true" v-show="xData.length"/>
         </view>
@@ -78,7 +112,7 @@
         <view class="table-container">
           <!-- 固定列 -->
           <view class="fixed-column">
-            <view class="fixed-header">
+            <view class="fixed-header" style="width: 260rpx;">
               <text class="header-cell fixed">上报时间</text>
             </view>
             <view class="fixed-body">
@@ -86,6 +120,7 @@
                 v-for="(item, index) in historyData"
                 :key="index"
                 class="fixed-row"
+                style="width: 260rpx;"
                 :class="{ even: index % 2 === 0 }"
               >
                 <text class="body-cell fixed">{{ formatTime(item.addtime) }}</text>
@@ -107,20 +142,19 @@
                 v-for="(item, index) in historyData"
                 :key="index"
                 class="scrollable-row"
-                :class="{ even: index % 2 === 0 }"
               >
-                <text class="body-cell">
+                <view class="body-cell">
                   <view class="status-icon" :class="item.ds == 1 ? 'success-icon' : 'status-icon'"></view>
-                  {{ item.ds == 1 ? '打开' : '关闭' }}</text>
-                <text class="body-cell">{{ item.at }}</text>
-                <text class="body-cell">{{ item.ah }}</text>
-                <text class="body-cell">
+                  {{ item.ds == 1 ? '打开' : '关闭' }}</view>
+                <view class="body-cell">{{ item.at }}</view>
+                <view class="body-cell">{{ item.ah }}</view>
+                <view class="body-cell">
                   <view class="status-icon" :class="item.batStatus == 0 ? 'success-icon' : 'status-icon'"></view>
-                  {{ item.batStatus == 0 ? '正常' : '欠压' }}</text>
-                <text class="body-cell">
+                  {{ item.batStatus == 0 ? '正常' : '欠压' }}</view>
+                <view class="body-cell">
                   <view class="status-icon" :class="item.work_sta == 0 ? 'warning-icon' : item.work_sta == 1 ? 'success-icon':'status-icon'"></view>
-                  {{ item.work_sta == 0 ? '待机' : item.work_sta == 1 ? '收集' : item.work_sta == 2 ? '培养' : '拍照' }}</text>
-                <text class="body-cell">{{ item.dver }}</text>
+                  {{ item.work_sta == 0 ? '待机' : item.work_sta == 1 ? '收集' : item.work_sta == 2 ? '培养' : '拍照' }}</view>
+                <view class="body-cell">{{ item.dver }}</view>
               </view>
             </view>
           </view>
@@ -313,6 +347,8 @@ export default {
       photo_iccid:'',
 	    chartData: {},
 	    chartKey: 0,
+      isStatic:false,
+      staticObj:{},
 	    objItem:{}
     };
   },
@@ -341,6 +377,11 @@ export default {
     },
     deviceInfo:{
       handler(newVal, oldVal){
+        this.isStatic = false
+        if(newVal.type_id == 7 && newVal.device_model == 52){
+          this.isStatic = true
+          this.getStatic()
+        }
         if (newVal.device_model == '11'){
           this.devImg = 'cbd4.1'
           this.isShowPhoto = true
@@ -381,6 +422,20 @@ export default {
     },
   },
   methods: {
+    formartWorkSta(work_sta){
+      return this.equipStateDict.work_sta_bzy2.value[work_sta]
+    },
+    async getStatic(){
+      const res = await this.$myRequest({
+        url: '/api/api_gateway?method=forecast.bzy.device_status',
+        method: 'POST',
+        data: {
+          device_type_id: this.deviceInfo.type,
+          id: this.deviceInfo.d_id,
+        },
+      });
+      this.staticObj = res
+    },
     async openPhoto(){
        const res = await this.$myRequest({
         url: '/api/api_gateway?method=forecast.send_control.admin_device_control',
@@ -755,14 +810,11 @@ export default {
   }
 }
 .status-icon{
-  position: absolute;
-  top: 50%;
-  transform: translateY(-50%);
-  left: 20rpx;
   width: 12rpx;
   height: 12rpx;
   border-radius: 50%;
   background: #B6BECA;
+  margin-right: 8rpx;
 }
 .success-icon{
   background: #0BBC58;
@@ -770,6 +822,9 @@ export default {
 .warning-icon{
   background: #F8B610;
 }
+.error-icon{
+  background: #FF6B6B;
+}
 .general-images{
   display: flex;
   align-items: center;
@@ -828,7 +883,8 @@ export default {
 
   .data-column-left {
     flex: 1;
-    display: flex;
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
     gap: 24rpx;
 
     .data-item {
@@ -953,6 +1009,10 @@ export default {
     color: #0bbc58;
   }
 }
+//滚动条隐藏
+::-webkit-scrollbar {
+  display: none;
+}
 /* 图表区域 */
 .chart-section {
   background: #FFFFFF;
@@ -1111,10 +1171,6 @@ export default {
             display: flex;
             align-items: center;
 
-            &.even {
-              background: #FAFAFA;
-            }
-
             .body-cell.fixed {
               font-size: 24rpx;
               font-family: 'Source Han Sans CN VF', sans-serif;
@@ -1167,10 +1223,6 @@ export default {
             height: 80rpx;
             align-items: center;
 
-            &.even {
-              background: #FAFAFA;
-            }
-
             .body-cell {
               min-width: 140rpx;
               font-size: 24rpx;
@@ -1179,7 +1231,9 @@ export default {
               color: #042118;
               text-align: center;
               margin-right: 10rpx;
-              position: relative;
+              display:flex;
+              align-items: center;
+              justify-content: center;
             }
           }
         }

+ 1 - 1
pages/bzy/detail.vue

@@ -228,7 +228,7 @@ export default {
     },
     handleSettingClick(){
       uni.navigateTo({
-        url: '/pages/bzy/deviceControl?deviceId=' + this.deviceInfo.id + '&d_id=' + this.deviceInfo.d_id,
+        url: '/pages/bzy/deviceControl?deviceId=' + this.deviceInfo.id + '&d_id=' + this.deviceInfo.d_id + '&device_model=' + this.deviceInfo.device_model,
       });
     },
     closeOperationHandler(){

+ 281 - 25
pages/bzy/deviceControl.vue

@@ -27,6 +27,7 @@
             <view class="device-detail-btn" @click="setDeviceContorl('dtu_update')">升级</view>
             <view class="device-detail-btn" @click="setDeviceContorl('dtu_reboot')">重启</view>
             <!-- <view class="device-detail-btn" @click="showMqttConfig">MQTT配置</view> -->
+            <view class="device-detail-btn" @click="setDeviceContorl('yfwlw')">平台切换</view>
           </view>
         </view>
         <view class="device-detail-item">
@@ -39,9 +40,9 @@
         <view class="device-detail-item">
           <text class="device-detail-label">强制操作</text>
           <view class="device-detail-btn-container">
-            <view class="device-detail-btn force-btn" @click="setDeviceContorl('takephoto')">拍照</view>
             <view class="device-detail-btn force-btn" @click="setDeviceContorl('update')">升级</view>
             <view class="device-detail-btn force-btn" @click="setDeviceContorl('reboot')">重启</view>
+            <view class="device-detail-btn force-btn" @click="setDeviceContorl('takephoto')">立即拍照</view>
             <view class="device-detail-btn force-btn" @click="setDeviceContorl('autotakephoto')">对焦拍照</view>
             <view class="device-detail-btn force-btn" @click="setDeviceContorl('turn')">转仓</view>
           </view>
@@ -60,21 +61,120 @@
           <view class="end-time" @click="showPickerHandler(index,'end_time')">{{item.end_time_label || '结束时间'}}
           </view><u-icon name="clock" color="#4E5969" size="26" style="padding-right: 20rpx"></u-icon>
         </view>
-        <view class="device-detail-viewImage">
-          <text class="device-detail-label">高温保护阀值(℃)
-          {{ equipContrlForm.tph }}</text>
+        <view class="device-detail-viewImage" v-if="device_model == 51">
+          <text class="device-detail-label">高温保护阀值(℃) {{ sliderField === 'tph' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.tph }}</text>
         </view>
-        <u-slider v-model="equipContrlForm.tph" style="width:100%" :min="50" :max="70" active-color="#0bbc58"></u-slider>
-        <view class="device-detail-viewImage">
-          <text class="device-detail-label">低温保护阀值(℃)
-          {{ equipContrlForm.tpl }}</text>
+        <view class="slider-container" v-if="device_model == 51">
+          <view class="slider-min-value">50</view>
+          <view
+            class="custom-progress"
+            @touchstart.stop="onSliderTouchStart($event, 'tph', 50, 70, 1)"
+            @touchmove.stop.prevent="onSliderTouchMove($event, 50, 70, 1)"
+            @touchend.stop="onSliderTouchEnd"
+            @tap.stop="onSliderTap($event, 'tph', 50, 70, 1)"
+          >
+            <view class="progress-track">
+              <view class="progress-fill" :style="{ width: getProgressWidth(sliderField === 'tph' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.tph, 50, 70) + '%' }"></view>
+              <view class="progress-thumb" :style="{ left: getProgressWidth(sliderField === 'tph' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.tph, 50, 70) + '%' }"></view>
+            </view>
+          </view>
+          <view class="slider-max-value">70</view>
+        </view>
+        <view class="device-detail-viewImage" v-if="device_model == 0">
+          <text class="device-detail-label">载玻片滴液时间(h) {{ sliderField === 'drop_time' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.drop_time }}</text>
+        </view>
+        <view class="slider-container" v-if="device_model == 0">
+          <view class="slider-min-value">1</view>
+          <view
+            class="custom-progress"
+            @touchstart.stop="onSliderTouchStart($event, 'drop_time', 1, 30, 1)"
+            @touchmove.stop.prevent="onSliderTouchMove($event, 1, 30, 1)"
+            @touchend.stop="onSliderTouchEnd"
+            @tap.stop="onSliderTap($event, 'drop_time', 1, 30, 1)"
+          >
+            <view class="progress-track">
+              <view class="progress-fill" :style="{ width: getProgressWidth(sliderField === 'drop_time' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.drop_time, 1, 30) + '%' }"></view>
+              <view class="progress-thumb" :style="{ left: getProgressWidth(sliderField === 'drop_time' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.drop_time, 1, 30) + '%' }"></view>
+            </view>
+          </view>
+          <view class="slider-max-value">30</view>
+        </view>
+        <view class="device-detail-viewImage" v-if="device_model == 0">
+          <text class="device-detail-label">孢子培养时间(h) {{ sliderField === 'cul_time' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.cul_time }}</text>
+        </view>
+        <view class="slider-container" v-if="device_model == 0">
+          <view class="slider-min-value">1</view>
+          <view
+            class="custom-progress"
+            @touchstart.stop="onSliderTouchStart($event, 'cul_time', 1, 24, 1)"
+            @touchmove.stop.prevent="onSliderTouchMove($event, 1, 24, 1)"
+            @touchend.stop="onSliderTouchEnd"
+            @tap.stop="onSliderTap($event, 'cul_time', 1, 24, 1)"
+          >
+            <view class="progress-track">
+              <view class="progress-fill" :style="{ width: getProgressWidth(sliderField === 'cul_time' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.cul_time, 1, 24) + '%' }"></view>
+              <view class="progress-thumb" :style="{ left: getProgressWidth(sliderField === 'cul_time' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.cul_time, 1, 24) + '%' }"></view>
+            </view>
+          </view>
+          <view class="slider-max-value">24</view>
+        </view>
+        <view class="device-detail-viewImage" v-if="device_model == 0">
+          <text class="device-detail-label">保温仓设定温度(℃) {{ sliderField === 'set_temp' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.set_temp }}</text>
+        </view>
+        <view class="slider-container" v-if="device_model == 0">
+          <view class="slider-min-value">10</view>
+          <view
+            class="custom-progress"
+            @touchstart.stop="onSliderTouchStart($event, 'set_temp', 10, 40, 1)"
+            @touchmove.stop.prevent="onSliderTouchMove($event, 10, 40, 1)"
+            @touchend.stop="onSliderTouchEnd"
+            @tap.stop="onSliderTap($event, 'set_temp', 10, 40, 1)"
+          >
+            <view class="progress-track">
+              <view class="progress-fill" :style="{ width: getProgressWidth(sliderField === 'set_temp' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.set_temp, 10, 40) + '%' }"></view>
+              <view class="progress-thumb" :style="{ left: getProgressWidth(sliderField === 'set_temp' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.set_temp, 10, 40) + '%' }"></view>
+            </view>
+          </view>
+          <view class="slider-max-value">40</view>
+        </view>
+        <view class="device-detail-viewImage" v-if="device_model == 51">
+          <text class="device-detail-label">低温保护阀值(℃) {{ sliderField === 'tpl' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.tpl }}</text>
+        </view>
+        <view class="slider-container" v-if="device_model == 51">
+          <view class="slider-min-value">-30</view>
+          <view
+            class="custom-progress"
+            @touchstart.stop="onSliderTouchStart($event, 'tpl', -30, 20, 1)"
+            @touchmove.stop.prevent="onSliderTouchMove($event, -30, 20, 1)"
+            @touchend.stop="onSliderTouchEnd"
+            @tap.stop="onSliderTap($event, 'tpl', -30, 20, 1)"
+          >
+            <view class="progress-track">
+              <view class="progress-fill" :style="{ width: getProgressWidth(sliderField === 'tpl' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.tpl, -30, 20) + '%' }"></view>
+              <view class="progress-thumb" :style="{ left: getProgressWidth(sliderField === 'tpl' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.tpl, -30, 20) + '%' }"></view>
+            </view>
+          </view>
+          <view class="slider-max-value">20</view>
         </view>
-        <u-slider v-model="equipContrlForm.tpl" style="width:100%" :min="-30" :max="20" active-color="#0bbc58"></u-slider>
         <view class="device-detail-viewImage">
-          <text class="device-detail-label">数据上传间隔(min)
-          {{ equipContrlForm.datt }}</text>
+          <text class="device-detail-label">数据上传间隔(min) {{ sliderField === 'datt' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.datt }}</text>
+        </view>
+        <view class="slider-container">
+          <view class="slider-min-value">10</view>
+          <view
+            class="custom-progress"
+            @touchstart.stop="onSliderTouchStart($event, 'datt', 10, 60, 10)"
+            @touchmove.stop.prevent="onSliderTouchMove($event, 10, 60, 10)"
+            @touchend.stop="onSliderTouchEnd"
+            @tap.stop="onSliderTap($event, 'datt', 10, 60, 10)"
+          >
+            <view class="progress-track">
+              <view class="progress-fill" :style="{ width: getProgressWidth(sliderField === 'datt' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.datt, 10, 60) + '%' }"></view>
+              <view class="progress-thumb" :style="{ left: getProgressWidth(sliderField === 'datt' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.datt, 10, 60) + '%' }"></view>
+            </view>
+          </view>
+          <view class="slider-max-value">60</view>
         </view>
-        <u-slider v-model="equipContrlForm.datt" style="width:100%;" :max="60" :min="10" active-color="#0bbc58"></u-slider>
       </view>
     </view>
     <view class="device-detail-btn-footer" v-if="activeTab === 'viewImage'">
@@ -130,11 +230,19 @@ export default {
       show: false,
       checked: false,
       currentIndex: -1,
-      currentType: ''
+      currentType: '',
+      sliderDragValue: null,
+      sliderRect: null,
+      sliderField: '',
+      sliderMin: 0,
+      sliderMax: 100,
+      device_model: '',
+      sliderStep: 1
     }
   },
   onLoad(options){
     this.d_id = options.d_id;
+    this.device_model = options.device_model;
     this.getControlDeviceConfigInfo();
     this.timeList = [];
     for(let i = 0;i<24;i++){
@@ -151,11 +259,95 @@ export default {
           this.activeTab = 'pestAnalysis'
         }else{
           this.activeTab = 'viewImage'
+          this.$nextTick(() => {
+            this.cacheSliderRect()
+          })
         }
       }
     })
   },
   methods: {
+    getProgressWidth(value, min, max) {
+      if (max === min) return 0
+      return ((value - min) / (max - min)) * 100
+    },
+    cacheSliderRect() {
+      const query = uni.createSelectorQuery().in(this)
+      query.select('.custom-progress').boundingClientRect(rect => {
+        if (rect) {
+          this.sliderRect = rect
+        }
+      }).exec()
+    },
+    onSliderTouchStart(e, field, min, max, step) {
+      this.sliderField = field
+      this.sliderMin = min
+      this.sliderMax = max
+      this.sliderStep = step || 1
+      this._lastUpdateTime = 0
+      this._touchX = e.touches[0].clientX
+      if (!this.sliderRect) {
+        const query = uni.createSelectorQuery().in(this)
+        query.select('.custom-progress').boundingClientRect(rect => {
+          this.sliderRect = rect
+          this._applyDragValue(e.touches[0].clientX)
+        }).exec()
+      } else {
+        this._applyDragValue(e.touches[0].clientX)
+      }
+    },
+    onSliderTouchMove(e, min, max, step) {
+      if (!this.sliderField || !this.sliderRect) return
+      this._touchX = e.touches[0].clientX
+      const now = Date.now()
+      if (now - this._lastUpdateTime < 33) return
+      this._lastUpdateTime = now
+      this._applyDragValue(e.touches[0].clientX)
+    },
+    onSliderTouchEnd(e) {
+      const clientX = (e.changedTouches && e.changedTouches[0])
+        ? e.changedTouches[0].clientX
+        : this._touchX
+      if (this.sliderField && this.sliderRect && clientX != null) {
+        const ratio = Math.max(0, Math.min(1,
+          (clientX - this.sliderRect.left) / this.sliderRect.width))
+        const value = this._snapToStep(Math.round(this.sliderMin + ratio * (this.sliderMax - this.sliderMin)))
+        this.$set(this.equipContrlForm, this.sliderField, value)
+      }
+      this.sliderField = ''
+      this.sliderDragValue = null
+      this._lastUpdateTime = 0
+      this._touchX = 0
+    },
+    onSliderTap(e, field, min, max, step) {
+      const snap = step || 1
+      const apply = (rect) => {
+        const ratio = Math.max(0, Math.min(1, e.detail.x / rect.width))
+        const raw = Math.round(min + ratio * (max - min))
+        const snapped = Math.round((raw - min) / snap) * snap + min
+        this.$set(this.equipContrlForm, field, Math.max(min, Math.min(max, snapped)))
+      }
+      if (!this.sliderRect) {
+        const query = uni.createSelectorQuery().in(this)
+        query.select('.custom-progress').boundingClientRect(rect => {
+          this.sliderRect = rect
+          apply(rect)
+        }).exec()
+      } else {
+        apply(this.sliderRect)
+      }
+    },
+    _applyDragValue(clientX) {
+      const ratio = Math.max(0, Math.min(1,
+        (clientX - this.sliderRect.left) / this.sliderRect.width))
+      const raw = Math.round(this.sliderMin + ratio * (this.sliderMax - this.sliderMin))
+      this.sliderDragValue = this._snapToStep(raw)
+    },
+    _snapToStep(value) {
+      const step = this.sliderStep || 1
+      const snapped = Math.round((value - this.sliderMin) / step) * step + this.sliderMin
+      return Math.max(this.sliderMin, Math.min(this.sliderMax, snapped))
+    },
     clearTime(){
       this.coll_time.forEach(item => {
         item.start_time = '';
@@ -232,6 +424,11 @@ export default {
     },
     handleTabClick(tab) {
       this.activeTab = tab;
+      if (tab === 'viewImage') {
+        this.$nextTick(() => {
+          this.cacheSliderRect()
+        })
+      }
     },
     showMqttConfig(){
       this.show = true;
@@ -241,7 +438,7 @@ export default {
     },
     async setDeviceContorl(type){
       const data = {
-        device_type_id: 3,
+        device_type_id: 7,
         d_id: this.d_id,
         cmd: type,
       }
@@ -271,22 +468,22 @@ export default {
     },
     saveSettings(){
       let newForm = Object.assign({}, this.equipContrlForm) // 深拷贝
-      newForm.st = newForm.st + ''
-      newForm.et = newForm.et + ''
-      newForm.st && newForm.st.slice(0, 2).charAt(0) != '0'
-        ? newForm.st.slice(0, 2)
-        : newForm.st.slice(1, 2)
-      newForm.et =
-        newForm.et && newForm.et.slice(0, 2).charAt(0) != '0'
-          ? newForm.et.slice(0, 2)
-          : newForm.et.slice(1, 2)
+      // newForm.st = newForm.st + ''
+      // newForm.et = newForm.et + ''
+      // newForm.st && newForm.st.slice(0, 2).charAt(0) != '0'
+      //   ? newForm.st.slice(0, 2)
+      //   : newForm.st.slice(1, 2)
+      // newForm.et =
+      //   newForm.et && newForm.et.slice(0, 2).charAt(0) != '0'
+      //     ? newForm.et.slice(0, 2)
+      //     : newForm.et.slice(1, 2)
 
       for (let k in newForm) {
         if (typeof newForm[k] === 'number') {
           newForm[k] = newForm[k] + ''
         }
       }
-      newForm.st = newForm.st.replace(':00', '')
+      // newForm.st = newForm.st.replace(':00', '')
       console.log(this.coll_time,'coll_timecoll_timecoll_time')
       const coll_time = [];
       for(let i = 0;i< this.coll_time.length;i++){
@@ -296,11 +493,24 @@ export default {
         }
       }
       newForm.coll_time = coll_time
+      newForm.admin = Number(newForm.admin);
+      newForm.drop_time = Number(newForm.drop_time);
+      newForm.cul_time = Number(newForm.cul_time);
+      newForm.set_temp = Number(newForm.set_temp);
+      newForm.datt = Number(newForm.datt);
+      newForm.cold_sw = Number(newForm.cold_sw);
+      newForm.on_off = Number(newForm.on_off);
+      newForm.tpl = Number(newForm.tpl);
+      newForm.tph = Number(newForm.tph);
+      if(this.device_model == 0){
+        delete newForm.tpl
+        delete newForm.tph
+      }
       this.$myRequest({
         url: '/api/api_gateway?method=forecast.send_control.device_control',
         method: 'POST',
         data: {
-          device_type_id: 3,
+          device_type_id: 7,
           d_id: this.d_id,
           config: JSON.stringify(newForm)
         }
@@ -449,6 +659,52 @@ export default {
       }
     }
   }
+  .slider-container{
+    position: relative;
+    margin-bottom: 20rpx;
+    .slider-min-value{
+      position: absolute;
+      left: 0;
+      top: -50rpx;
+    }
+    .slider{
+      width: 600rpx;
+    }
+    .slider-max-value{
+      position: absolute;
+      right: 0;
+      top: -50rpx;
+    }
+  }
+  .custom-progress{
+    width: 600rpx;
+    padding: 0;
+    .progress-track{
+      position: relative;
+      height: 12rpx;
+      background-color: #ebedf0;
+      border-radius: 6rpx;
+    }
+    .progress-fill{
+      position: absolute;
+      left: 0;
+      top: 0;
+      height: 100%;
+      background-color: #0BBC58;
+      border-radius: 6rpx;
+    }
+    .progress-thumb{
+      position: absolute;
+      top: 50%;
+      width: 28rpx;
+      height: 28rpx;
+      margin-left: -14rpx;
+      margin-top: -14rpx;
+      border-radius: 50%;
+      background-color: #fff;
+      box-shadow: 0 1px 4px rgba(0,0,0,0.3);
+    }
+  }
   .device-detail-content{
     display: flex;
     padding: 24rpx 32rpx;

+ 48 - 46
pages/bzy/devicePhoto.vue

@@ -12,24 +12,14 @@
     </view>
 
     <!-- 日期选择器 -->
-     <view class="date-container">
-        <!-- <view class="select-year">
-          <view class="select-year-item" @click="showPicker = true">
-            {{ currentYear }}
-            <u-icon name="arrow-down" size="18" class="arrow-down"></u-icon>
-          </view>
-        </view> -->
+     <!-- <view class="date-container">
         <view class="date-picker" @click="show = true">
           <view class="date-input">
             <text class="date-label">{{ time_begin || '-' }}</text>
           </view>
-          <!-- <view class="date-separator">-</view>
-          <view class="date-input">
-            <text class="date-label">{{ time_end || '-' }}</text>
-          </view> -->
           <u-icon name="calendar" class="calendar"></u-icon>
         </view>
-    </view>
+    </view> -->
     <!-- 主图片区域 -->
     <view class="main-photo" v-if="currentImg.addr">
       <movable-area class="movable-area" :style="{ height: containerHeight }">
@@ -95,17 +85,17 @@
     <view class="recognition-result" v-if="pestList.length">
       <view class="result-title">当前图片识别结果</view>
       <!-- 一类害虫 -->
-      <view class="pest-category" v-for="(pest,index) in pestList" :key="index">
+      <view class="pest-category">
         <view class="category-header">
-          <text class="category-title">{{ pest[0] }}</text>
+          <text class="category-title">孢子名称</text>
           <text class="category-count">数量</text>
         </view>
-        <view class="pest-item" v-for="(p, i) in pest[1]" :key="i">
+        <view class="pest-item" v-for="(pest,index) in pestList" :key="index">
           <view class="pest-info">
-            <view class="pest-color" :style="{ backgroundColor: getPestColor(p.pest_name) }"></view>
-            <text class="pest-name">{{ p.pest_name }}</text>
+            <view class="pest-color"></view>
+            <text class="pest-name">{{ pest.text }}</text>
           </view>
-          <text class="pest-count">{{ p.pest_num }}</text>
+          <text class="pest-count">{{ pest.count }}</text>
         </view>
       </view>
     </view>
@@ -224,7 +214,6 @@ export default {
       this.time_end = e.result;
       this.pestList = [];
       await this.getDevicePhotoDetails()
-      this.currentImg = {};
     },
     getPestColor(pestName) {
       // 如果该害虫已有颜色,直接返回
@@ -314,6 +303,7 @@ export default {
         },
       });
       this.currentImg = res;
+      console.log(this.currentImg,'thiscurrentImg')
       // 处理label参数生成bugMarkers
       this.processBugMarkers();
       const pest_list = res.pest_list || [];
@@ -334,16 +324,6 @@ export default {
           })
         }
       }
-      pest_list.forEach(item => {
-        if(!pesAlltList.includes(item.pest_name)){
-          pestArr.get('其他害虫').push(item)
-        }
-      })
-      const pestList = []
-      for(let key of pestArr){
-        pestList.push(key)
-      }
-      this.pestList = pestList
     },
     onImageLoad(e) {
       this.imageWidth = e.detail.width;
@@ -384,38 +364,41 @@ export default {
 
       try {
         // 处理label参数,将单引号替换为双引号
-        let labelStr = this.currentImg.label;
-        let label
-        if(labelStr){
-          labelStr = labelStr?.replace(/'/g, '"');
-          console.log(labelStr,'labelStrlabelStr')
-          label = JSON.parse(labelStr);
-        }
+        // let labelStr = this.currentImg.label;
+        // let label
+        // if(labelStr){
+        //   labelStr = labelStr?.replace(/'/g, '"');
+        //   console.log(labelStr,'labelStrlabelStr')
+        //   label = JSON.parse(labelStr);
+        // }
         const markers = [];
 
         // 根据原图宽度计算缩放比例
-        let scaleRatio;
+        
+        let scaleRatio = 3.1;
         if (this.imageWidth >= 5000) {
-          scaleRatio = 1;
+          scaleRatio = 4;
         } else if (this.imageWidth >= 4000) {
           scaleRatio = 1;
         } else {
-          scaleRatio = 1;
+          scaleRatio = 2.39;
         }
         console.log('原图宽度:', this.imageWidth, '缩放比例:', scaleRatio);
         const mark = this.currentImg.mark || []
         if(this.currentImg.is_mark){
           mark.forEach(item=>{
-            const x = (item.startX * scaleRatio / this.imageWidth) * 100;
-            const y = (item.startY * scaleRatio / this.imageHeight) * 100;
-            const width = (item.width * scaleRatio / this.imageWidth) * 100;
-            const height = (item.height * scaleRatio / this.imageHeight) * 100;
+            const { startX, startY,text } = item;
+            const x = item.width > 0 ? (startX * scaleRatio / this.imageWidth) * 105 : ((startX + item.width) * scaleRatio / this.imageWidth) * 105;
+            const y = item.height > 0 ? (startY * scaleRatio / this.imageHeight) * 105 : ((startY + item.height) * scaleRatio / this.imageHeight) * 105;
+            const width = (item.width > 0 ? item.width : -item.width) * scaleRatio / this.imageWidth * 100;
+            const height = (item.height > 0 ? item.height : -item.height) * scaleRatio / this.imageHeight * 100;
             markers.push({
               id: item.text, // 显示虫子名字
               x,
               y,
               width,
               height,
+              text:item.text,
               color:this.getPestColor(item.text)
             });
           })
@@ -439,13 +422,33 @@ export default {
                 y,
                 width,
                 height,
-                color
+                color,
+                text:pestName,
               });
             }
           });
         }
         console.log(markers,'markersmarkers')
         this.bugMarkers = markers;
+        // 改成按照text合并,并把数量也合并
+        const pestArr = new Map()
+        markers.forEach(item=>{
+          if(!pestArr.has(item.text)){
+            pestArr.set(item.text, 1)
+          }else{
+            pestArr.set(item.text, pestArr.get(item.text) + 1)
+          }
+        })
+        console.log(pestArr,'pestArr')
+        const arr = []
+        for(let [key, value] of pestArr){
+          arr.push({
+            text:key,
+            count:value
+          })
+        }
+       
+        this.pestList = [...arr];
       } catch (error) {
         console.error('处理label参数失败:', error);
         this.bugMarkers = [];
@@ -608,10 +611,9 @@ export default {
   /* 主图片区域 */
   .main-photo {
     position: relative;
-    margin: 0 24rpx 24rpx;
+    margin: 24rpx 24rpx 24rpx;
     background-color: #FFFFFF;
     border-radius: 16rpx;
-    padding: 0;
     overflow: hidden;
     .movable-area {
       width: 100%;

+ 2 - 0
pages/cbd/components/deviceData.vue

@@ -1160,11 +1160,13 @@ export default {
             padding: 16rpx 0;
             border-bottom: 1rpx solid #F5F5F5;
             height: 80rpx;
+            width: 260rpx;
             display: flex;
             align-items: center;
 
             .body-cell.fixed {
               font-size: 24rpx;
+              width: 260rpx;
               font-family: 'Source Han Sans CN VF', sans-serif;
               font-weight: 400;
               color: #042118;

+ 1 - 1
pages/deviceDetails/weatherStation1/index.vue

@@ -207,7 +207,7 @@
                     class="table-cell"
                     v-for="(item1, index1) in tableColumn"
                     :key="index1"
-                    >{{ item[item1.prop] }}</view
+                    >{{ item[item1.prop] == 'NA' ? '--' : item[item1.prop] }}</view
                   >
                 </view>
               </view>

+ 7 - 7
pages/equipList2/index.vue

@@ -734,15 +734,15 @@ import openingDegree from '../assets/openingDegree.png';
 						break;
 					case 2:
 						item.addtime = item.uptime;
-						if(item.device_model == 102){
+						// if(item.device_model == 102){
 							uni.navigateTo({
 								url: '../scd/detail?info=' + JSON.stringify(item),
 							});
-						}else{
-							uni.navigateTo({
-								url: '../prevention/equipmentdetails?shebei=' + JSON.stringify(item),
-							});
-						}
+						// }else{
+						// 	uni.navigateTo({
+						// 		url: '../prevention/equipmentdetails?shebei=' + JSON.stringify(item),
+						// 	});
+						// }
 						break;
 					case 38:
 						uni.navigateTo({
@@ -847,7 +847,7 @@ import openingDegree from '../assets/openingDegree.png';
 						// uni.navigateTo({
 						// 	url: '../bzy/detail?info=' + JSON.stringify(item),
 						// });
-						if(item.pur_id == 458){
+						if(item.pur_id !== 307){
 							uni.navigateTo({
 								url: '../bzy/detail?info=' + JSON.stringify(item),
 							});

+ 18 - 51
pages/equipList2/seabox/modification.vue

@@ -38,7 +38,7 @@
 				<view class="modification__content__item__title">
 					设备位置
 				</view>
-				<view class="modification__content__item__input" @click.native="amendcity">
+				<view class="modification__content__item__input" @click="amendcity">
 					<view class="location-city">{{ city }}</view>
 					<u-icon name="arrow-right"></u-icon>
 				</view>
@@ -65,8 +65,6 @@
 				moddata: [],
 				city: "",
 				selectcityTF: false,
-				locationCity: "",
-				locationName: "",
 				quanxian:{
 					namealter:false,
 					cityalter:false,
@@ -121,9 +119,6 @@
 						title: '修改名称成功!',
 						icon: "none"
 					});
-					uni.removeStorage({
-						key: "location"
-					})
 					this.clickLeft()
 				}
 			},
@@ -160,42 +155,24 @@
 					}
 				})
 			},
-			getCity({city}){
-				if(city.city == undefined){
-					var add = city
-				}else if(city.city != undefined){
-					var add = city.city
-				}
-				let pages = getCurrentPages()
-				let prevpage = pages[pages.length - 2]
-				prevpage.$vm.city = add
-				uni.navigateBack({
-					delta: 1
-				})
-			},
-			selectedCity(city){
-				this.getCity && this.getCity({city});
-			},
-			location(){
-				let That = this;
-				uni.chooseLocation({
-				  success(res){
-						uni.setStorage({
-							key:"location",
-							data:[res.longitude,res.latitude]
-						})
-						That.city = res && res.address;
-						That.locationName = res && res.name;
-						That.selectedCity({city:That.city,name:That.locationName});
-				  },
-					fail(){
-						That.locationCity = "定位失败,请点击重试";
-						That.locationName = "";
-					}
-				});
-			},
 			amendcity() {
-				this.location()
+				const lat = this.moddata.lat
+				const lng = this.moddata.lng
+				uni.chooseLocation({
+					latitude: lat || undefined,
+					longitude: lng || undefined,
+					success: (res) => {
+						this.moddata.lat = res.latitude
+						this.moddata.lng = res.longitude
+						this.city = res.address || res.name
+						this.selectaddress(res.longitude, res.latitude)
+					},
+					fail: (err) => {
+						if (err && err.errMsg && err.errMsg.indexOf('cancel') === -1) {
+							uni.showToast({ title: "选择位置失败,请重试", icon: "none" })
+						}
+					},
+				})
 			},
 			selectaddress(lng, lat) { //获取分布位置
 				uni.request({
@@ -245,16 +222,6 @@
 				},
 			})
 		},
-		onShow(){
-			uni.getStorage({
-				key: "location",
-				success: (res) => {
-					this.moddata.lat = res.data[1]
-					this.moddata.lng = res.data[0]
-					this.selectaddress(this.moddata.lng, this.moddata.lat)
-				}
-			})
-		}
 	}
 </script>
 

+ 86 - 73
pages/scd/components/deviceData.vue

@@ -65,7 +65,7 @@
         </view> -->
       </view>
       <view class="chart-content">
-        <view class="chart-canvas-container" v-if="!show" :key="chartKey">
+        <view class="chart-canvas-container" v-show="!show" :key="chartKey">
           <!-- <div id="tempChart" class="chart-canvas"></div> -->
 		      <qiun-data-charts type="line" :chartData="chartData" :canvas2d="true" :inScrollView="true" :opts="opts" :ontouch="true" v-show="xData.length"/>
         </view>
@@ -81,14 +81,15 @@
         <view class="table-container">
           <!-- 固定列 -->
           <view class="fixed-column">
-            <view class="fixed-header">
-              <text class="header-cell fixed">上报时间</text>
+            <view class="fixed-header" style="width: 260rpx;">
+              <text class="header-cell fixed">上报时间</text> 
             </view>
             <view class="fixed-body">
               <view
                 v-for="(item, index) in historyData"
                 :key="index"
                 class="fixed-row"
+                style="width: 260rpx;"
               >
                 <text class="body-cell fixed">{{ formatTime(item.addtime) }}</text>
               </view>
@@ -104,15 +105,15 @@
               <text class="header-cell">倾倒状态</text>
               <text class="header-cell">灯管状态</text>
               <text class="header-cell">电压状态</text>
-              <text class="header-cell">定时状态</text>
-              <text class="header-cell">杀虫数量</text>
-              <text class="header-cell">风扇状态</text>
+              <text class="header-cell">定时</text>
+              <text class="header-cell" v-if="deviceInfo.device_model != 101 && deviceInfo.device_model != 102">电击次数</text>
+              <text class="header-cell" v-if="deviceInfo.device_model == 102">杀虫数量</text>
+              <text class="header-cell" v-if="deviceInfo.device_model != 0">风扇状态</text>
               <text class="header-cell">温度(°C)</text>
               <text class="header-cell">湿度(%)</text>
               <text class="header-cell">充电电压</text>
               <text class="header-cell">电池状态</text>
               <text class="header-cell">信号强度</text>
-              <text class="header-cell">上报时间</text>
             </view>
             <view class="scrollable-body">
               <view
@@ -120,46 +121,47 @@
                 :key="index"
                 class="scrollable-row"
               >
-                <text class="body-cell">
+                <view class="body-cell">
                   <view class="status-icon" :class="item.ds == '开机' ? 'success-icon' : 'status-icon'"></view>
                   {{ item.ds }}
-                </text>
-                <text class="body-cell">
+                </view>
+                <view class="body-cell">
                   <view class="status-icon" :class="item.ws == '工作' ? 'success-icon' : 'warning-icon'"></view>
                   {{ item.ws }}
-                </text>
-                <text class="body-cell">
+                </view>
+                <view class="body-cell">
                   <view class="status-icon" :class="item.rps == '正常' ? 'success-icon' : 'warning-icon'"></view>
                   {{ item.rps }}
-                </text>
-                <text class="body-cell">
+                </view>
+                <view class="body-cell">
                   <view class="status-icon" :class="item.tps == '正常' ? 'success-icon' : 'warning-icon'"></view>
                   {{ item.tps }}
-                </text>
-                <text class="body-cell">
+                </view>
+                <view class="body-cell">
                   <view class="status-icon" :class="item.tps == '正常' ? 'success-icon' : 'warning-icon'"></view>
                   {{ item.dps }}
-                </text>
-                <text class="body-cell">
+                </view>
+                <view class="body-cell">
                   <view class="status-icon" :class="item.voltval == '开灯' ? 'success-icon' : 'warning-icon'"></view>
                   {{ item.voltval }}
-                </text>
-                <text class="body-cell">
+                </view>
+                <view class="body-cell">
                   <view class="status-icon" :class="item.vps == 0 ? 'success-icon' : 'status-icon'"></view>
                   {{ item.vps == '0' ? '正常' : '欠压' }}
-                </text>
-                <text class="body-cell">
-                  <view class="status-icon" :class="item.tt == '常亮' ? 'success-icon' : 'warning-icon'"></view>
+                </view>
+                <view class="body-cell">
                   {{ item.tt }}
-                </text>
-                <text class="body-cell">{{ item.infr_ct }}</text>
-                <text class="body-cell">{{ getCtFormat(item.ct) }}</text>
-                <text class="body-cell">{{ item.at }}</text>
-                <text class="body-cell">{{ item.ah }}</text>
-                <text class="body-cell">{{ item.cv }}</text>
-                <text class="body-cell">{{ item.bv }}</text>
-                <text class="body-cell">{{ item.csq }}</text>
-                <text class="body-cell">{{ formatTime(item.addtime) }}</text>
+                </view>
+                <view class="body-cell" v-if="deviceInfo.device_model != 101">{{ item.infr_ct || item.ct }}</view>
+                <view class="body-cell" v-if="deviceInfo.device_model != 0">
+                  <view class="status-icon" :class="item.ct == 1 ? 'success-icon' : item.ct == 0 ? 'status-icon' : 'warning-icon'"></view>
+                  {{ getCtFormat(item.ct) }}
+                </view>
+                <view class="body-cell">{{ item.at }}</view>
+                <view class="body-cell">{{ item.ah }}</view>
+                <view class="body-cell">{{ item.cv }}</view>
+                <view class="body-cell">{{ item.bv }}</view>
+                <view class="body-cell">{{ item.csq }}</view>
               </view>
             </view>
           </view>
@@ -312,7 +314,7 @@ export default {
         { name: '湿度', id: 'new_hum' },
         { name: '充电电压', id: 'cv' },
         { name: '电池电压', id: 'bv' },
-        { name: '杀虫数量', id: 'infr_ct' },
+        { name: '电击次数', id: 'infr_ct' },
       ],
       opts: {
         type: 'line',
@@ -365,9 +367,6 @@ export default {
     totalPages(){
       return Math.ceil(this.totalPage / this.page_size)
     },
-    formatDevImg() {
-      return `https://s3.hnyfwlw.com/webstaticimg/bigdata_app/image/${this.devImg}.png`;
-    },
   },
   watch:{
     polylineList:{
@@ -383,25 +382,31 @@ export default {
     },
     deviceInfo:{
       handler(newVal, oldVal){
-        if (newVal.device_model == '11'){
-          this.devImg = 'cbd4.1'
-          this.isShowPhoto = true
-        } else if(newVal.device_model == '12'){
-          this.devImg = 'cbd1'
-          this.isShowPhoto = false
-        } else if(newVal.device_model == '13'){
-          this.devImg = 'cbd5'
-          this.isShowPhoto = true
-        } else if(newVal.device_model == '14'){
-          this.devImg = 'cbd6'
-          this.isShowPhoto = true
-        } else if(newVal.device_model == '15'){
-          this.devImg = 'gkcbd1'
-          this.isShowPhoto = false
-        } else{
-          this.devImg = 'cbd'
-          this.isShowPhoto = true
+        if(newVal.device_model != 101 && newVal.device_model != 102){
+          this.chartTabs = [
+            { name: '温度', id: 'new_tem' },
+            { name: '湿度', id: 'new_hum' },
+            { name: '充电电压', id: 'cv' },
+            { name: '电池电压', id: 'bv' },
+            { name: '电击次数', id: 'infr_ct' },
+          ]
+        }else if(newVal.device_model == 102){
+          this.chartTabs = [
+            { name: '温度', id: 'new_tem' },
+            { name: '湿度', id: 'new_hum' },
+            { name: '充电电压', id: 'cv' },
+            { name: '电池电压', id: 'bv' },
+            { name: '杀虫数量', id: 'infr_ct' },
+          ]
+        }else{
+          this.chartTabs = [
+            { name: '温度', id: 'new_tem' },
+            { name: '湿度', id: 'new_hum' },
+            { name: '充电电压', id: 'cv' },
+            { name: '电池电压', id: 'bv' },
+          ]
         }
+        this.isShowPhoto = true
       },
       deep:true,
       immediate:true,
@@ -523,11 +528,12 @@ export default {
       this.deviceInfoStatic = res
     },
     async refreshData(){
+      console.log(this.deviceInfo,'deviceInfodeviceInfo')
       const res = await this.$myRequest({
         url: '/api/api_gateway?method=forecast.send_control.get_device_config',
         method: 'POST',
         data: {
-          device_type_id: this.deviceInfo.type,
+          device_type_id: this.deviceInfo.type_id,
           d_id: this.deviceInfo.d_id,
           control_type:'data',
           device_model: this.deviceInfo.device_model || 0,
@@ -699,8 +705,8 @@ export default {
       } else if (this.chartTabs[index].id === 'bv') {
         this.yData = this.polylineList.map(item => Number(item.others.bv) || 0);
       } else if (this.chartTabs[index].id === 'infr_ct') {
-        this.yData = this.polylineList.map(item => Number(item.others.infr_ct) || 0);
-      }
+        this.yData = this.polylineList.map(item => Number(item.others.infr_ct) || Number(item.others.ct) || 0);
+      } 
       this.$nextTick(() => {
         // this.drawChart();
 		    this.updateChartsData();
@@ -862,6 +868,22 @@ export default {
     left: -70rpx;
   }
 }
+.status-icon{
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  left: 20rpx;
+  width: 12rpx;
+  height: 12rpx;
+  border-radius: 50%;
+  background: #B6BECA;
+}
+.success-icon{
+  background: #0BBC58;
+}
+.warning-icon{
+  background: #F8B610;
+}
 /* 主要数据面板 */
 .main-data-panel {
   display: flex;
@@ -981,22 +1003,6 @@ export default {
     }
   }
 }
-.status-icon{
-  position: absolute;
-  top: 50%;
-  transform: translateY(-50%);
-  left: 20rpx;
-  width: 12rpx;
-  height: 12rpx;
-  border-radius: 50%;
-  background: #B6BECA;
-}
-.success-icon{
-  background: #0BBC58;
-}
-.warning-icon{
-  background: #F8B610;
-}
 .tabs-container{
   display:flex;
   align-items: center;
@@ -1012,6 +1018,11 @@ export default {
     color: #0bbc58;
   }
 }
+
+//滚动条隐藏
+::-webkit-scrollbar {
+  display: none;
+}
 /* 图表区域 */
 .chart-section {
   background: #FFFFFF;
@@ -1167,10 +1178,12 @@ export default {
             padding: 16rpx 0;
             border-bottom: 1rpx solid #F5F5F5;
             height: 80rpx;
+            width: 260rpx;
             display: flex;
             align-items: center;
             .body-cell.fixed {
               font-size: 24rpx;
+              width: 260rpx;
               font-family: 'Source Han Sans CN VF', sans-serif;
               font-weight: 400;
               color: #042118;

+ 103 - 38
pages/scd/deviceControl.vue

@@ -116,7 +116,7 @@
         </view>
         <view class="device-detail-viewImage">
           <text class="device-detail-label">数据间隔(min)
-          {{ equipContrlForm.dattim }}</text>
+          {{ sliderField === 'dattim' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.dattim }}</text>
         </view>
         <!-- <view class="slider-container">
           <view class="slider-min-value">50</view>
@@ -127,21 +127,21 @@
           <view class="slider-min-value">10</view>
           <view
             class="custom-progress"
-            @touchstart.stop="onSliderTouchStart($event, 'dattim', 10, 120)"
-            @touchmove.stop.prevent="onSliderTouchMove($event, 10, 120)"
+            @touchstart.stop="onSliderTouchStart($event, 'dattim', 10, 120, 10)"
+            @touchmove.stop.prevent="onSliderTouchMove($event, 10, 120, 10)"
             @touchend.stop="onSliderTouchEnd"
-            @tap.stop="onSliderTap($event, 'dattim', 10, 120)"
+            @tap.stop="onSliderTap($event, 'dattim', 10, 120, 10)"
           >
             <view class="progress-track">
-              <view class="progress-fill" :style="{ width: getProgressWidth(equipContrlForm.dattim, 10, 120) + '%' }"></view>
-              <view class="progress-thumb" :style="{ left: getProgressWidth(equipContrlForm.dattim, 10, 120) + '%' }"></view>
+              <view class="progress-fill" :style="{ width: getProgressWidth(sliderField === 'dattim' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.dattim, 10, 120) + '%' }"></view>
+              <view class="progress-thumb" :style="{ left: getProgressWidth(sliderField === 'dattim' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.dattim, 10, 120) + '%' }"></view>
             </view>
           </view>
           <view class="slider-max-value">120</view>
         </view>
         <view class="device-detail-viewImage">
           <text class="device-detail-label">清虫间隔(min)
-          {{ equipContrlForm.clt }}</text>
+          {{ sliderField === 'clt' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.clt }}</text>
         </view>
         <!-- <view class="slider-container">
           <view class="slider-min-value">-30</view>
@@ -152,14 +152,14 @@
           <view class="slider-min-value">10</view>
           <view
             class="custom-progress"
-            @touchstart.stop="onSliderTouchStart($event, 'clt', 10, 120)"
-            @touchmove.stop.prevent="onSliderTouchMove($event, 10, 120)"
+            @touchstart.stop="onSliderTouchStart($event, 'clt', 10, 120, 1)"
+            @touchmove.stop.prevent="onSliderTouchMove($event, 10, 120, 1)"
             @touchend.stop="onSliderTouchEnd"
-            @tap.stop="onSliderTap($event, 'clt', 10, 120)"
+            @tap.stop="onSliderTap($event, 'clt', 10, 120, 1)"
           >
             <view class="progress-track">
-              <view class="progress-fill" :style="{ width: getProgressWidth(equipContrlForm.clt, 10, 120) + '%' }"></view>
-              <view class="progress-thumb" :style="{ left: getProgressWidth(equipContrlForm.clt, 10, 120) + '%' }"></view>
+              <view class="progress-fill" :style="{ width: getProgressWidth(sliderField === 'clt' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.clt, 10, 120) + '%' }"></view>
+              <view class="progress-thumb" :style="{ left: getProgressWidth(sliderField === 'clt' && sliderDragValue !== null ? sliderDragValue : equipContrlForm.clt, 10, 120) + '%' }"></view>
             </view>
           </view>
           <view class="slider-max-value">120</view>
@@ -258,7 +258,13 @@ export default {
       showTimeLangPicker: false,
       currentIndex: -1,
       myuser_type: false,
-      currentType: ''
+      currentType: '',
+      sliderDragValue: null,
+      sliderRect: null,
+      sliderField: '',
+      sliderMin: 0,
+      sliderMax: 100,
+      sliderStep: 1
     }
   },
   onLoad(options){
@@ -279,6 +285,9 @@ export default {
           this.activeTab = 'pestAnalysis'
         }else{
           this.activeTab = 'viewImage'
+          this.$nextTick(() => {
+            this.cacheSliderRect()
+          })
         }
       }
     })
@@ -288,36 +297,87 @@ export default {
       if (max === min) return 0
       return ((value - min) / (max - min)) * 100
     },
-    onSliderTouchStart(e, field, min, max) {
-      this.sliderField = field
-      this.sliderMin = min
-      this.sliderMax = max
+    // Cache slider rect once to avoid async query on every touch
+    cacheSliderRect() {
       const query = uni.createSelectorQuery().in(this)
       query.select('.custom-progress').boundingClientRect(rect => {
-        this.sliderRect = rect
+        if (rect) {
+          this.sliderRect = rect
+        }
       }).exec()
-      this.updateSliderValue(e.touches[0].clientX, field, min, max)
     },
-    onSliderTouchMove(e, min, max) {
+    onSliderTouchStart(e, field, min, max, step) {
+      this.sliderField = field
+      this.sliderMin = min
+      this.sliderMax = max
+      this.sliderStep = step || 1
+      this._lastUpdateTime = 0
+      this._touchX = e.touches[0].clientX
+      if (!this.sliderRect) {
+        const query = uni.createSelectorQuery().in(this)
+        query.select('.custom-progress').boundingClientRect(rect => {
+          this.sliderRect = rect
+          this._applyDragValue(e.touches[0].clientX)
+        }).exec()
+      } else {
+        this._applyDragValue(e.touches[0].clientX)
+      }
+    },
+    onSliderTouchMove(e, min, max, step) {
       if (!this.sliderField || !this.sliderRect) return
-      this.updateSliderValue(e.touches[0].clientX, this.sliderField, min, max)
+      // Always track the latest finger position (non-reactive, no setData cost)
+      this._touchX = e.touches[0].clientX
+      // Throttle: only push to the renderer at ~30fps to avoid flooding the bridge
+      const now = Date.now()
+      if (now - this._lastUpdateTime < 33) return
+      this._lastUpdateTime = now
+      this._applyDragValue(e.touches[0].clientX)
     },
-    onSliderTouchEnd() {
+    onSliderTouchEnd(e) {
+      // Use the final finger position from changedTouches, falling back to tracked _touchX
+      const clientX = (e.changedTouches && e.changedTouches[0])
+        ? e.changedTouches[0].clientX
+        : this._touchX
+      if (this.sliderField && this.sliderRect && clientX != null) {
+        const ratio = Math.max(0, Math.min(1,
+          (clientX - this.sliderRect.left) / this.sliderRect.width))
+        const value = this._snapToStep(Math.round(this.sliderMin + ratio * (this.sliderMax - this.sliderMin)))
+        this.$set(this.equipContrlForm, this.sliderField, value)
+      }
       this.sliderField = ''
+      this.sliderDragValue = null
+      this._lastUpdateTime = 0
+      this._touchX = 0
     },
-    onSliderTap(e, field, min, max) {
-      const query = uni.createSelectorQuery().in(this)
-      query.select('.custom-progress').boundingClientRect(rect => {
-        this.sliderRect = rect
-        this.updateSliderValue(e.detail.x + rect.left, field, min, max)
-      }).exec()
+    onSliderTap(e, field, min, max, step) {
+      const snap = step || 1
+      const apply = (rect) => {
+        const ratio = Math.max(0, Math.min(1, e.detail.x / rect.width))
+        const raw = Math.round(min + ratio * (max - min))
+        const snapped = Math.round((raw - min) / snap) * snap + min
+        this.$set(this.equipContrlForm, field, Math.max(min, Math.min(max, snapped)))
+      }
+      if (!this.sliderRect) {
+        const query = uni.createSelectorQuery().in(this)
+        query.select('.custom-progress').boundingClientRect(rect => {
+          this.sliderRect = rect
+          apply(rect)
+        }).exec()
+      } else {
+        apply(this.sliderRect)
+      }
     },
-    updateSliderValue(clientX, field, min, max) {
-      if (!this.sliderRect) return
-      let ratio = (clientX - this.sliderRect.left) / this.sliderRect.width
-      ratio = Math.max(0, Math.min(1, ratio))
-      const value = Math.round(min + ratio * (max - min))
-      this.$set(this.equipContrlForm, field, value)
+    // Non-reactive visual update — only touches sliderDragValue (flat scalar = cheap setData)
+    _applyDragValue(clientX) {
+      const ratio = Math.max(0, Math.min(1,
+        (clientX - this.sliderRect.left) / this.sliderRect.width))
+      const raw = Math.round(this.sliderMin + ratio * (this.sliderMax - this.sliderMin))
+      this.sliderDragValue = this._snapToStep(raw)
+    },
+    _snapToStep(value) {
+      const step = this.sliderStep || 1
+      const snapped = Math.round((value - this.sliderMin) / step) * step + this.sliderMin
+      return Math.max(this.sliderMin, Math.min(this.sliderMax, snapped))
     },
     confirmTimePicker(e){
       const value = e[0].value;
@@ -368,6 +428,11 @@ export default {
     },
     handleTabClick(tab) {
       this.activeTab = tab;
+      if (tab === 'viewImage') {
+        this.$nextTick(() => {
+          this.cacheSliderRect()
+        })
+      }
     },
     showMqttConfig(){
       this.show = true;
@@ -401,9 +466,11 @@ export default {
     },
     saveSettings(){
       let newForm = Object.assign({}, this.equipContrlForm) // 深拷贝
-      newForm.ds = newForm.ds ? 1 : 0
+      newForm.ds = newForm.ds ? '1' : '0'
       newForm.st = this.start_time
       newForm.et = this.end_time
+      newForm.clt = String(newForm.clt)
+      newForm.dattim = String(newForm.dattim)
       this.$myRequest({
         url: '/api/api_gateway?method=forecast.send_control.device_control',
         method: 'POST',
@@ -572,7 +639,7 @@ export default {
   }
   .device-detail-content{
     display: flex;
-    padding: 12rpx;
+    padding: 24rpx;
     flex-direction: column;
     align-items: flex-start;
     gap: 20rpx;
@@ -658,7 +725,6 @@ export default {
       height: 100%;
       background-color: #0BBC58;
       border-radius: 6rpx;
-      transition: width 0.2s;
     }
     .progress-thumb{
       position: absolute;
@@ -670,7 +736,6 @@ export default {
       border-radius: 50%;
       background-color: #fff;
       box-shadow: 0 1px 4px rgba(0,0,0,0.3);
-      transition: left 0.2s;
     }
   }
   .device-detail-btn-footer{

+ 2 - 0
pages/sy/components/deviceData.vue

@@ -1133,10 +1133,12 @@ export default {
             border-bottom: 1rpx solid #F5F5F5;
             height: 80rpx;
             display: flex;
+            width: 260rpx;
             align-items: center;
 
             .body-cell.fixed {
               font-size: 24rpx;
+              width: 260rpx;
               font-family: 'Source Han Sans CN VF', sans-serif;
               font-weight: 400;
               color: #042118;

BIN
static/images/location_dot.png


+ 10 - 0
static/js/equipState_dict.json

@@ -423,6 +423,16 @@
         },
         "desc":"摄像头状态 0 待机, 1 收集, 2 培养, 3 拍照"
     },
+    "work_sta_bzy2": {
+        "name": "状态",
+        "unit": "",
+        "value": {
+        "0": "待机",
+        "1": "采集",
+        "2": "拍照"
+        },
+        "desc": "状态 0 待机, 1 收集, 2 拍照"
+    },
     "imgres":{
         "name":"图片分辨率",
         "unit":"",