Просмотр исходного кода

feat: 新增气象站设备管理功能,优化设备控制逻辑

1. 新增设备详情子页面:weatherStation1页面组,包含设备控制、SIM卡详情、设备报修页面
2. 新增多终端设备控制页面适配,根据用户权限显示管理员tab
3. 替换API接口地址,更新请求参数格式
4. 新增设备操作弹窗菜单,优化设备数据展示逻辑
5. 新增图片上传补标标识功能
6. 调整设备列表跳转逻辑,适配新的设备详情页面
allen 1 неделя назад
Родитель
Сommit
c7c5a16fd9

+ 41 - 0
pages.json

@@ -128,6 +128,13 @@
 						"navigationStyle": "custom"
 					}
 				},{
+					"path": "deviceControl2",
+					"style": {
+						"navigationBarTitleText": "设备控制",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				},{
 					"path": "devicePhoto",
 					"style": {
 						"navigationBarTitleText": "查看图片",
@@ -1495,6 +1502,40 @@
 					"navigationStyle": "custom"
 				}
 			}]
+		},
+		{
+			"root": "pages/deviceDetails/weatherStation1",
+			"pages": [{
+				"path": "index",
+				"style": {
+					"enablePullDownRefresh": false,
+					"navigationStyle": "custom"
+				}
+			},{
+				"path": "devControl",
+				"style": {
+					"enablePullDownRefresh": false,
+					"navigationStyle": "custom"
+				}
+			},{
+				"path": "simDetail",
+				"style": {
+					"enablePullDownRefresh": false,
+					"navigationStyle": "custom"
+				}
+			},{
+				"path": "devRepair",
+				"style": {
+					"enablePullDownRefresh": false,
+					"navigationStyle": "custom"
+				}
+			},{
+				"path": "eleDetail",
+				"style": {
+					"enablePullDownRefresh": false,
+					"navigationStyle": "custom"
+				}
+			}]
 		}
 	],
 	"condition": {

+ 13 - 1
pages/bzy/deviceControl.vue

@@ -12,7 +12,7 @@
     <view class="device-detail__body">
       <view class="tabs">
         <view class="tab-container">
-          <view class="tab-item" :class="activeTab === 'pestAnalysis'?'active':''" @click="handleTabClick('pestAnalysis')">
+          <view class="tab-item" v-if="myuser_type" :class="activeTab === 'pestAnalysis'?'active':''" @click="handleTabClick('pestAnalysis')">
             管理员
           </view>
           <view class="tab-item" :class="activeTab === 'viewImage'?'active':''" @click="handleTabClick('viewImage')">
@@ -134,6 +134,7 @@ export default {
       equipContrlForm: {},
       time: '',
       activeTab: 'pestAnalysis',
+      myuser_type: false,
       title: '设置控制',
       deviceType: '',
       location: '',
@@ -154,6 +155,17 @@ export default {
         label:i < 10 ? '0' + i + ':00' : i + ':00'
       })
     }
+    uni.getStorage({
+      key:"myuser_type",
+      success:(res)=>{
+        if(Number(res.data) == 1){
+          this.myuser_type = true
+          this.activeTab = 'pestAnalysis'
+        }else{
+          this.activeTab = 'viewImage'
+        }
+      }
+    })
   },
   methods: {
     clearTime(){

+ 25 - 0
pages/cb/cbd/equip-set/imgpage.vue

@@ -42,6 +42,7 @@
 						<image :src="$imageURL+'/bigdata_app'+'/image/cb/jiazai.ui.gif'" mode="">
 						</image>
 						<image :src="item.addr" mode="" @click="examine(index)"></image>
+						<view class="text-addr" v-if="isShowBu(item) && myuser_type">补</view>
 					</view>
 					<view class="imglist_right">
 						<view class="icon_box" @click="delimg(item.ids)">
@@ -100,6 +101,7 @@
 					second: false
 				},
 				flag: 1,
+				myuser_type: false,
 				self: true,
 				isTop: false,
 				resultdata: {},
@@ -111,6 +113,14 @@
 			}
 		},
 		methods: {
+			isShowBu(image){
+				const addr = image?.addr || ''
+				// 如果里面字段包含history就显示补
+				if(addr.indexOf('history') != -1){
+					return true;
+				}
+				return false;
+			},
 			// 切换模型
 			confirmModel(e) {
 				this.modelList.forEach((item, index) => {
@@ -322,6 +332,14 @@
 					console.log(this.myuid)
 				}
 			})
+			uni.getStorage({
+				key:"myuser_type",
+				success:(res)=>{
+					if(Number(res.data) == 1){
+						this.myuser_type = true
+					}
+				}
+			})
 			console.log(option)
 			this.device_id = option.device_id
 			this.d_id = option.d_id
@@ -501,6 +519,13 @@
 				width: 100%;
 				height: 280rpx;
 			}
+			.text-addr{
+				position: absolute;
+				top: 10rpx;
+				left: 10rpx;
+				color:#f00;
+				font-size: 24rpx;
+			}
 		}
 
 		.imglist_right {

+ 13 - 1
pages/cbd/deviceControl.vue

@@ -12,7 +12,7 @@
     <view class="device-detail__body">
       <view class="tabs">
         <view class="tab-container">
-          <view class="tab-item" :class="activeTab === 'pestAnalysis'?'active':''" @click="handleTabClick('pestAnalysis')">
+          <view class="tab-item" v-if="myuser_type" :class="activeTab === 'pestAnalysis'?'active':''" @click="handleTabClick('pestAnalysis')">
             管理员
           </view>
           <view class="tab-item" :class="activeTab === 'viewImage'?'active':''" @click="handleTabClick('viewImage')">
@@ -142,6 +142,7 @@ export default {
       time: '',
       activeTab: 'pestAnalysis',
       title: '设置控制',
+      myuser_type: false,
       deviceType: '',
       location: '',
       d_id: '',
@@ -152,6 +153,17 @@ export default {
   onLoad(options){
     this.d_id = options.d_id
     this.getControlDeviceConfigInfo()
+    uni.getStorage({
+      key:"myuser_type",
+      success:(res)=>{
+        if(Number(res.data) == 1){
+          this.myuser_type = true
+          this.activeTab = 'pestAnalysis'
+        }else{
+          this.activeTab = 'viewImage'
+        }
+      }
+    })
   },
   methods: {
     handleBack() {

+ 103 - 2
pages/deviceDetails/weatherStation/index.vue

@@ -8,6 +8,28 @@
 
     <!-- 设备基本信息 -->
     <view class="device-info">
+      <view class="operation" @click.stop="isShowOperation = !isShowOperation">操作</view>
+      <view class="operation-container" v-if="isShowOperation" @click="closeOperationHandler">
+      <view class="operation-background"></view>
+      <view class="operation-content">
+        <view class="operation-item" @click="goPage('devRepair')">
+          <image :src="serviceIcon" class="operation-icon"></image>
+          维修
+        </view>
+        <view class="operation-item" @click="goPage('devControl')">
+          <image :src="settingIcon" class="operation-icon"></image>
+          设置
+        </view>
+        <view class="operation-item" @click="goPage('simDetail')">
+          <image :src="simIcon" class="operation-icon"></image>
+          SIM卡
+        </view>
+        <!-- <view class="operation-item" @click="modification">
+          <image :src="editIcon" class="operation-icon"></image>
+          修改
+        </view> -->
+      </view>
+    </view>
       <view @click="copy(deviceInfo.devBid)" class="info-item">
         <text class="info-label">设备ID</text>
         <text class="info-value">{{ deviceInfo.devBid }}</text>
@@ -102,7 +124,7 @@
               <u-icon name="reload" color="#0BBC58" style="margin-left: 12rpx" @click="refreshData"></u-icon>
             </view>
           <!-- {{formatDate(deviceInfo.uptime)}} -->
-            <view class="control-right">
+            <!-- <view class="control-right">
               <image
                 class="control-img"
                 src="/static/images/cb/control.svg"
@@ -121,7 +143,7 @@
                 mode="aspectFit"
                 @click="goPage('simDetail')"
               ></image>
-            </view>
+            </view> -->
           </view>
           <view class="params-board">
             <view class="params-left">
@@ -241,6 +263,7 @@ export default {
   data() {
     return {
       deviceInfo: {},
+      isShowOperation: false,
       activeTab: 'monitoring',
       monitorList: [],
       statusList: [],
@@ -249,6 +272,12 @@ export default {
       startDate: '',
       endDate: '',
       page: 1,
+      photoIcon:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/photoIcon.png',
+      editIcon:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/editIcon.png',
+      serviceIcon:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/serviceIcon.png',
+      simIcon:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/simIcon.png',
+      settingIcon:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/settingIcon.png',
+      isShowPhoto:false,
       pageSize: 9,
       total: 0,
       tableData: [],
@@ -261,6 +290,9 @@ export default {
     },
   },
   methods: {
+    closeOperationHandler(){
+      this.isShowOperation = false;
+    },
     async refreshData(){
       //api/api_gateway?method=qxz.device.control
       await this.$myRequest({
@@ -498,6 +530,7 @@ export default {
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
+  text-align: center;
 }
 /* 设备基本信息 */
 .device-info {
@@ -516,6 +549,74 @@ export default {
     margin-bottom: 0;
   }
 
+
+.operation-container{
+  position: fixed;
+  right: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 99;
+}
+.operation{
+  position: fixed;
+  top: 260rpx;
+  right: 0;
+  z-index: 999;
+  width:48rpx;
+  height: 100rpx;
+  line-height: 50rpx;
+  border-radius: 8px 0 0 8px;
+  border-top: 4rpx solid #FFF;
+  border-bottom: 4rpx solid #FFF;
+  border-left: 4rpx solid #FFF;
+  background: #dddfe6a3;
+  color: #515153;
+  text-align: center;
+  font-family: "Source Han Sans CN VF";
+  font-size: 24rpx;
+  font-weight: 500;
+  writing-mode: vertical-rl;
+}
+.operation-background{
+  position: fixed;
+  right: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 998;
+  background: #00000040;
+}
+.operation-content{
+  position: fixed;
+  top: 240rpx;
+  right: 80rpx;
+  height: 126rpx;
+  display: flex;
+  padding: 16rpx 32rpx;
+  align-items: center;
+  gap: 64rpx;
+  z-index: 999;
+  border-radius: 32rpx;
+  border: 2rpx solid #FFF;
+  background: #FFF;
+  backdrop-filter: blur(8rpx);
+  .operation-item{
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    color: #333333;
+    text-align: center;
+    font-family: "Source Han Sans CN VF";
+    font-size: 24rpx;
+    font-weight: 400;
+  }
+  .operation-icon{
+    width: 58rpx;
+    height: 58rpx;
+  }
+}
   .info-label {
     width: 116rpx;
     color: #999999;

+ 121 - 0
pages/deviceDetails/weatherStation1/charts.vue

@@ -0,0 +1,121 @@
+<template>
+  <view>
+    <canvas
+      canvas-id="orIfOOkumJYtFcnuJkjxcySENqRtXceW"
+      id="orIfOOkumJYtFcnuJkjxcySENqRtXceW"
+      class="charts"
+      @touchstart="touchstart"
+      @touchmove="touchmove"
+      @touchend="touchend"
+    />
+  </view>
+</template>
+
+<script>
+import uCharts from "../../../components/js_sdk/u-charts/u-charts/u-charts.js";
+var uChartsInstance = {};
+export default {
+  data() {
+    return {
+      cWidth: 320,
+      cHeight: 200,
+    };
+  },
+  onReady() {
+    //这里的 750 对应 css .charts 的 width
+    // this.cWidth = uni.upx2px(340);
+    //这里的 500 对应 css .charts 的 height
+    // this.cHeight = uni.upx2px(200);
+    this.getServerData();
+  },
+  methods: {
+    getServerData() {
+      //模拟从服务器获取数据时的延时
+      setTimeout(() => {
+        //模拟服务器返回数据,如果数据格式和标准格式不同,需自行按下面的格式拼接
+        let res = {
+          categories: ["2018", "2019", "2020", "2021", "2022", "2023"],
+          series: [
+            {
+              name: "成交量A",
+              linearColor: [
+                [0, "#1890FF"],
+                [0.25, "#00B5FF"],
+                [0.5, "#00D1ED"],
+                [0.75, "#00E6BB"],
+                [1, "#90F489"],
+              ],
+              data: [35, 8, 25, 37, 4, 20],
+            },
+          ],
+        };
+        this.drawCharts("orIfOOkumJYtFcnuJkjxcySENqRtXceW", res);
+      }, 500);
+    },
+    drawCharts(id, data) {
+      const ctx = uni.createCanvasContext(id, this);
+      uChartsInstance[id] = new uCharts({
+        type: "line",
+        context: ctx,
+        width: this.cWidth,
+        height: this.cHeight,
+        categories: data.categories,
+        series: data.series,
+        animation: true,
+        background: "#FFFFFF",
+        color: [
+          "#1890FF",
+          "#91CB74",
+          "#FAC858",
+          "#EE6666",
+          "#73C0DE",
+          "#3CA272",
+          "#FC8452",
+          "#9A60B4",
+          "#ea7ccc",
+        ],
+        padding: [10, 10, 10, 10],
+        enableScroll: true,
+        legend: {
+          position: "top",
+        },
+        xAxis: {
+          disableGrid: true,
+          scrollShow: true,
+          itemCount: 4,
+        },
+        yAxis: {
+          gridType: "dash",
+          dashLength: 2,
+        },
+        dataLabel: false,
+        extra: {
+          line: {
+            type: "curve",
+            width: 2,
+            activeType: "hollow",
+          },
+        },
+      });
+    },
+    touchstart(e) {
+      uChartsInstance[e.target.id].scrollStart(e);
+    },
+    touchmove(e) {
+      uChartsInstance[e.target.id].scroll(e);
+    },
+    touchend(e) {
+      uChartsInstance[e.target.id].scrollEnd(e);
+      uChartsInstance[e.target.id].touchLegend(e);
+      uChartsInstance[e.target.id].showToolTip(e);
+    },
+  },
+};
+</script>
+
+<style scoped>
+.charts {
+  width: 750rpx;
+  height: 500rpx;
+}
+</style>

+ 171 - 0
pages/deviceDetails/weatherStation1/devControl.vue

@@ -0,0 +1,171 @@
+<template>
+  <view class="device-details-page">
+    <cu-custom :isBack="true">
+      <template slot="content">
+        <view class="nav-title">设备控制</view>
+      </template>
+    </cu-custom>
+    <view class="control-board">
+      <view class="title">操作</view>
+      <div style="margin-bottom: 48rpx;">
+        <u-button type="success" class="reload-btn" @click="handleControl('RESET')">重启</u-button>
+      </div>
+      <u-button type="success" class="reload-btn" @click="handleControl('FWUPDATE')">升级</u-button>
+      <view class="title date-title">本地存储间隔(min)</view>
+      <view class="date-slider">
+        <slider
+          v-model="storageVal"
+          show-value="true"
+          :min="1"
+          :max="240"
+          @change="changeStorageVal"
+          block-color="#57C878"
+          activeColor="#57C878"
+          step="1"
+        />
+      </view>
+       <u-button type="success" class="sub-btn" @click="handleControl('STORAGE_INR')">确定</u-button>
+      <view class="title date-title">数据传输间隔(min)</view>
+      <view class="date-slider">
+        <slider
+          v-model="dataInterval"
+          show-value="true"
+          :min="1"
+          :max="240"
+          @change="changeDataInterval"
+          block-color="#57C878"
+          activeColor="#57C878"
+          step="1"
+        />
+      </view>
+       <u-button type="success" class="sub-btn" @click="handleControl('INTERVAL')">确定</u-button>
+    </view>
+    <!-- <view class="footer-board">
+      <u-button type="success" class="sub-btn" @click="handleSubmit()">确定</u-button>
+    </view> -->
+  </view>
+</template>
+
+<script>
+export default {
+  onLoad(options) {
+    const deviceInfo = JSON.parse(decodeURIComponent(options.deviceInfo));
+    console.log(deviceInfo, "deviceInfo");
+    this.deviceInfo = deviceInfo;
+    this.getDevControlParams();
+  },
+  data() {
+    return {
+      deviceInfo: {},
+      storageVal: "",
+      dataInterval: "",
+    };
+  },
+  methods: {
+    changeDataInterval(v){
+      this.dataInterval = v.detail.value
+    },
+    changeStorageVal(v){
+      this.storageVal = v.detail.value
+    },
+    // 获取设备控制参数信息
+    async getDevControlParams() {
+      await this.$myRequest({
+        url: '/api/api_gateway?method=qxz.device.control_args',
+        data: {
+          device_id: this.deviceInfo.devBid,
+        }
+      }).then(res => {
+        console.log('设备控制参数信息', res)
+        this.storageVal = res.storage_inr
+        this.dataInterval = res.interval
+      })
+    },
+    async handleControl(cmd) {
+      const params = {
+        device_id: this.deviceInfo.devBid,
+        cmd,
+        interval: cmd === 'INTERVAL' ? this.dataInterval : undefined,
+        storage_inr: cmd === 'STORAGE_INR' ? this.storageVal : undefined,
+        stnid: cmd === 'STNID' ? this.stnidVal : undefined
+      };
+      await this.$myRequest({
+        url: '/api/api_gateway?method=qxz.device.control',
+        data: params
+      }).then(res => {
+        console.log('设备控制参数信息', res)
+        uni.showToast({
+          title: "操作成功",
+          icon: "none",
+        });
+      })
+    },
+    async handleSubmit() {
+      await this.handleControl('STORAGE_INR');
+      await this.handleControl('INTERVAL');
+      uni.showToast({
+        title: "操作成功",
+        icon: "none",
+      });
+    }
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.device-details-page {
+  background: linear-gradient(
+      180deg,
+      #ffffff00 0%,
+      #f5f6fa 23.64%,
+      #f5f6fa 100%
+    ),
+    linear-gradient(102deg, #bfeadd 6.77%, #b8f1e7 40.15%, #b9eef5 84.02%);
+  min-height: 100vh;
+  padding: 0 32rpx;
+  padding-top: 98rpx;
+}
+.nav-title{
+  text-align: center;
+}
+.control-board {
+  border-radius: 16rpx;
+  background: #fff;
+  padding: 32rpx;
+  margin-top: 46rpx;
+  .title {
+    margin-bottom: 16rpx;
+    color: #303133;
+    font-size: 28rpx;
+    font-weight: 400;
+  }
+  .date-title {
+    margin-bottom: 52rpx;
+  }
+  .reload-btn {
+    width: 100%;
+    background: #0bbc58;
+    margin-bottom: 48rpx;
+    border-radius: 16rpx;
+  }
+  .date-slider {
+    
+  }
+}
+
+.footer-board {
+  border-top: 2rpx solid #e4e7ed;
+  background: #fff;
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 164rpx;
+  padding: 16rpx 32rpx 0 32rpx;
+  .sub-btn {
+    width: 100%;
+    background: #0bbc58;
+    border-radius: 16rpx;
+  }
+}
+</style>

+ 350 - 0
pages/deviceDetails/weatherStation1/devRepair.vue

@@ -0,0 +1,350 @@
+<template>
+  <view class="device-repair-page">
+    <cu-custom :isBack="true">
+      <template slot="content">
+        <view class="nav-title">设备报修</view>
+      </template>
+    </cu-custom>
+    <view class="form-board">
+      <u-form
+        ref="baseFormRef"
+        :model="baseForm"
+        labelWidth="162"
+        :rules="rules"
+        errorType="toast"
+      >
+        <view class="repaid-board">
+          <view class="title">基本信息</view>
+          <u-form-item label="设备ID" prop="device_id" required>
+            <u-input
+              v-model="baseForm.device_id"
+              placeholder="设备ID"
+              disabled
+            ></u-input>
+          </u-form-item>
+          <u-form-item label="设备名称" prop="devName" required>
+            <u-input
+              v-model="baseForm.devName"
+              placeholder="请输入设备名称"
+              disabled
+            ></u-input>
+          </u-form-item>
+          <u-form-item label="联系人" prop="user" required>
+            <u-input
+              v-model="baseForm.user"
+              placeholder="请输入"
+            ></u-input>
+          </u-form-item>
+          <u-form-item label="联系人电话" prop="userphone" required>
+            <u-input
+              v-model="baseForm.userphone"
+              placeholder="请输入"
+              type="number"
+            ></u-input>
+          </u-form-item>
+          <u-form-item label="联系人地址" prop="addr" required>
+            <u-input
+              v-model="baseForm.addr"
+              placeholder="请输入"
+            ></u-input>
+          </u-form-item>
+        </view>
+        <view class="repaid-board">
+          <view class="title">报修详情</view>
+          <u-input
+            v-model="baseForm.errordesc"
+            placeholder="请描述设备问题"
+            type="textarea"
+            :height="216"
+            maxlength="140"
+            show-word-limit
+          ></u-input>
+
+          <view class="upload-section">
+            <view class="upload-title"
+              >设备照片<text class="info">(最多4张)</text></view
+            >
+            <u-upload
+              :file-list="fileList"
+              :max-count="4"
+              @on-choose-complete="onChooseComplete"
+              @on-remove="onRemove"
+              name="upload"
+              :max-size="5 * 1024 * 1024"
+              accept="image/*"
+              :auto-upload="false"
+              :show-progress="false"
+            >
+              <view slot="addBtn" class="add-btn">
+                <u-icon name="plus" size="56" color="#909399"></u-icon>
+              </view>
+            </u-upload>
+          </view>
+        </view>
+      </u-form>
+    </view>
+
+    <view class="footer-board">
+      <u-button type="success" class="sub-btn" @click="submitForm"
+        >确定</u-button
+      >
+    </view>
+  </view>
+</template>
+
+<script>
+import uInput from "../../../uview-ui/components/u-input/u-input.vue";
+export default {
+  components: { uInput },
+  onLoad(options) {
+    const deviceInfo = JSON.parse(decodeURIComponent(options.deviceInfo));
+    console.log("deviceInfo:", deviceInfo);
+    this.baseForm.device_id = deviceInfo.devBid
+    this.baseForm.devName = deviceInfo.devName
+  },
+  data() {
+    return {
+      baseForm: {
+        device_id: "",
+        devName: "",
+        user: "",
+        userphone: "",
+        addr: "",
+        errordesc: "",
+      },
+      rules: {
+        devName: [
+          {
+            required: true,
+            message: "请输入设备名称",
+            trigger: ["blur", "change"],
+          },
+        ],
+        user: [
+          {
+            required: true,
+            message: "请输入联系人",
+            trigger: ["blur", "change"],
+          },
+        ],
+        userphone: [
+          {
+            required: true,
+            message: "请输入联系人电话",
+            trigger: ["blur", "change"],
+          },
+          {
+            pattern: /^1[3-9]\d{9}$/,
+            message: "请输入正确的手机号",
+            trigger: ["blur", "change"],
+          },
+        ],
+        addr: [
+          {
+            required: true,
+            message: "请输入联系人地址",
+            trigger: ["blur", "change"],
+          },
+        ],
+        errordesc: [
+          {
+            required: true,
+            message: "请描述设备问题",
+            trigger: ["blur", "change"],
+          },
+        ],
+      },
+      fileList: [],
+    };
+  },
+  methods: {
+    onChooseComplete(lists) {
+      const newFiles = lists.filter(
+        (item) => !this.fileList.some((f) => f.url === item.url)
+      );
+      newFiles.forEach((file) => {
+        const item = {
+          url: file.url,
+          status: "uploading",
+        };
+        this.fileList.push(item);
+        const index = this.fileList.length - 1;
+        uni.uploadFile({
+          url: "https://wx.hnyfwlw.com/api/api_gateway?method=base.bases.base_photo",
+          filePath: file.url,
+          name: "img_file",
+          formData: {
+            user: "test",
+          },
+          success: (uploadFileRes) => {
+            const data = JSON.parse(uploadFileRes.data);
+            if (data.data && data.data.src) {
+              this.fileList[index].status = "success";
+              this.fileList[index].src = data.data.src;
+              this.$forceUpdate();
+              console.log("上传成功,图片地址:", data.data.src);
+            } else {
+              this.fileList[index].status = "failed";
+              uni.showToast({
+                title: data.msg || "上传失败",
+                icon: "none",
+              });
+            }
+          },
+          fail: (err) => {
+            this.fileList[index].status = "failed";
+            uni.showToast({
+              title: "上传失败",
+              icon: "none",
+            });
+            console.error("上传失败:", err);
+          },
+        });
+      });
+    },
+    onRemove(index) {
+      this.fileList.splice(index, 1);
+    },
+    submitForm() {
+      const hasUploading = this.fileList.some((f) => f.status === "uploading");
+      if (hasUploading) {
+        uni.showToast({
+          title: "请等待图片上传完成",
+          icon: "none",
+        });
+        return;
+      }
+      const hasFailed = this.fileList.some((f) => f.status === "failed");
+      if (hasFailed) {
+        uni.showToast({
+          title: "有图片上传失败,请重新上传",
+          icon: "none",
+        });
+        return;
+      }
+      this.$refs.baseFormRef
+        .validate()
+        .then((res) => {
+          const uploadedImages = this.fileList
+            .filter((f) => f.status === "success" && f.src)
+            .map((f) => f.src);
+          const params = {
+            ...this.baseForm,
+            errorimg: uploadedImages,
+          };
+          console.log("提交参数:", params);
+          // 
+          this.$myRequest({
+            url: "/api/api_gateway?method=after_sale.after_sale_manage.aftersale_apply",
+            data: params,
+          }).then(res => {
+            console.log("aftersale_apply:", res);
+            if(res){
+              uni.showToast({
+                  title: '提交成功',
+                  duration: 2000,
+              });
+              setTimeout(()=>{
+                uni.navigateBack({
+                  delta:1
+                })
+              },1000)
+            }else{
+              uni.showToast({
+                  title: '提交失败',
+                  duration: 2000,
+                icon:"none"
+              });
+            }
+          })
+        })
+        .catch((errors) => {
+          console.log("表单验证失败", errors);
+        });
+    },
+  },
+};
+</script>
+
+<style lang="scss">
+.device-repair-page {
+  background:
+    linear-gradient(180deg, #ffffff00 0%, #f5f6fa 23.64%, #f5f6fa 100%),
+    linear-gradient(102deg, #bfeadd 6.77%, #b8f1e7 40.15%, #b9eef5 84.02%);
+  min-height: 100vh;
+  padding: 0 32rpx;
+  padding-top: 98rpx;
+  padding-bottom: 200rpx;
+}
+
+.form-board {
+  margin-top: 46rpx;
+}
+.repaid-board {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 32rpx;
+  margin-bottom: 24rpx;
+
+  .title {
+    color: #020305;
+    font-size: 32rpx;
+    font-weight: bold;
+  }
+}
+
+.upload-section {
+  padding: 20rpx 0;
+
+  .upload-title {
+    font-size: 28rpx;
+    color: #303133;
+    margin-bottom: 16rpx;
+    .info {
+      color: #999;
+    }
+  }
+
+  .add-btn {
+    background: #f1f4f8;
+    width: 160rpx;
+    height: 160rpx;
+    border-radius: 12rpx;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+}
+
+// 使所有输入框文本右对齐
+::v-deep .uni-input-input,
+::v-deep .uni-input-placeholder {
+  text-align: right !important;
+}
+::v-deep .u-add-wrap {
+  display: none !important;
+}
+::v-deep .u-input__textarea {
+  background: #f1f4f8 !important;
+  border-radius: 8rpx;
+  margin-top: 24rpx;
+  padding: 17rpx 24rpx !important;
+}
+
+.footer-board {
+  border-top: 2rpx solid #e4e7ed;
+  background: #fff;
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 164rpx;
+  padding: 16rpx 32rpx 0 32rpx;
+
+  .sub-btn {
+    width: 100%;
+    background: #0bbc58;
+    border-radius: 16rpx;
+  }
+}
+</style>

+ 816 - 0
pages/deviceDetails/weatherStation1/eleDetail.vue

@@ -0,0 +1,816 @@
+<template>
+  <view class="ele-details-page">
+    <cu-custom :isBack="true">
+      <template slot="content">
+        <view class="nav-title">小气候监测站II监测要素</view>
+      </template>
+    </cu-custom>
+    <view class="date-picker" @click="openDatePicker">
+      <text>{{ startDate }}</text>
+      <text class="separator">-</text>
+      <text>{{ endDate }}</text>
+      <image class="date-icon" src="/static/images/device/date-active.svg" />
+    </view>
+    <u-calendar
+      v-model="show"
+      :mode="mode"
+      @change="onDateChange"
+      ref="calendar" 
+      range-color="#999"
+      btn-type="success"
+      active-bg-color="#0BBC58"
+      range-bg-color="rgba(11,188,88,0.13)"
+    ></u-calendar>
+    <view class="echarts-board">
+      <view class="period-tabs">
+        <view
+          class="tab-item"
+          :class="{ active: activePeriod === '24h' }"
+          @click="changePeriod('24h')"
+          >24小时</view
+        >
+        <view
+          class="tab-item"
+          :class="{ active: activePeriod === '7d' }"
+          @click="changePeriod('7d')"
+          >一周</view
+        >
+        <view
+          class="tab-item"
+          :class="{ active: activePeriod === '30d' }"
+          @click="changePeriod('30d')"
+          >一月</view
+        >
+      </view>
+
+      <view class="element-tabs" v-if="hasData">
+        <view class="ele-board">
+          <view
+            v-for="(item, index) in eleTabs"
+            :key="index"
+            class="ele-item"
+            :class="{ 'ele-active': eleActive === index }"
+            @click="changeElement(index)"
+            >{{ item.name }}</view
+          >
+        </view>
+        <view class="fold-icon" @click="toggleFold">
+          <u-icon :name="isFolded ? 'arrow-down' : 'arrow-up'"></u-icon>
+        </view>
+      </view>
+      <!-- 展开后的所有要素 -->
+      <view v-if="!isFolded && hasData" class="expanded-elements">
+        <view
+          class="expanded-item"
+          v-for="(item, index) in eleTabs"
+          :key="index"
+          :class="{ 'ele-active': eleActive === index }"
+          @click="changeElement(index)"
+        >
+          {{ item.name }}
+        </view>
+      </view>
+
+      <view class="chart-container" v-if="hasData">
+        <canvas
+          v-show="!show"
+          canvas-id="temperatureChart"
+          id="temperatureChart"
+          class="chart"
+          @touchstart="touchLineA($event)"
+          @touchmove="moveLineA($event)"
+          @touchend="touchEndLineA($event)"
+          disable-scroll="true"
+        ></canvas>
+      </view>
+      <view class="no-data" v-else>
+        <text>暂无数据</text>
+      </view>
+
+      <!-- <view class="stats-panel">
+        <view class="stat-item">
+          <text class="value">27<text class="unit">°C</text></text>
+          <text class="label">最低值</text>
+        </view>
+        <view class="stat-item">
+          <text class="value">32<text class="unit">°C</text></text>
+          <text class="label">平均值</text>
+        </view>
+        <view class="stat-item">
+          <text class="value red">42<text class="unit red">°C</text></text>
+          <text class="label">最高值</text>
+        </view>
+      </view> -->
+    </view>
+
+    <view class="history-section">
+      <view class="section-title">历史数据</view>
+      <view class="table-wrap">
+        <view class="fixed-column">
+          <view class="table-cell header">上报时间</view>
+          <view class="table-cell" v-for="(item, idx) in tableList" :key="idx">
+            {{ item.uptime }}
+          </view>
+        </view>
+        <view class="table-bg"></view>
+        <scroll-view class="scroll-column" scroll-x="true">
+          <view class="scroll-content">
+            <view class="table-row">
+              <view class="table-cell header">{{
+                activeChartInfo.name + "(" + activeChartInfo.unit + ")"
+              }}</view>
+            </view>
+            <view class="table-row" v-for="(item, idx) in tableList" :key="idx">
+              <view class="table-cell">{{ item[activeChartInfo.title] }}</view>
+            </view>
+          </view>
+        </scroll-view>
+      </view>
+
+      <view class="pagination">
+        <view
+          class="page-item prev"
+          @click="prevPage"
+          :class="{ disabled: currentPage === 1 }"
+          >上一页</view
+        >
+        <view class="page-info">
+          <text class="curret-page">{{ currentPage }}</text>
+          <text>/</text>
+          <text>{{ totalPages }}</text>
+        </view>
+        <view
+          class="page-item next"
+          @click="nextPage"
+          :class="{ disabled: currentPage >= totalPages }"
+          >下一页</view
+        >
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import uCharts from "../../../components/js_sdk/u-charts/u-charts/u-charts.js";
+import baseCharts from "./charts.vue";
+export default {
+  components: { baseCharts },
+  onLoad(options) {
+    const deviceInfo = JSON.parse(decodeURIComponent(options.deviceInfo));
+    this.eleKey = options.eleKey || "";
+    this.initDateRange(deviceInfo.uptime);
+    this.deviceInfo = deviceInfo;
+    this.getChartData();
+    this.getObData();
+  },
+  data() {
+    return {
+      temperatureChart: null,
+      activePeriod: "",
+      activeElement: "temperature",
+      show: false,
+      mode: "range",
+      startDate: "",
+      endDate: "",
+      isFolded: true, // 控制要素是否收起
+      eleTabs: [],
+      eleActive: 0,
+      hasData: true,
+      tableList: [],
+      currentPage: 1,
+      pageSize: 9,
+      total: 0,
+      chartData: {
+        categories: [],
+        series: [],
+      },
+      activeChartInfo: {},
+      eleKey: "",
+    };
+  },
+  computed: {
+    totalPages() {
+      return Math.ceil(this.total / this.pageSize);
+    },
+  },
+  methods: {
+    initDateRange(timestamp) {
+      if (!timestamp) {
+        const today = new Date();
+        this.endDate = this.formatDate(today);
+        const startDate = new Date(today);
+        startDate.setDate(startDate.getDate() - 7);
+        this.startDate = this.formatDate(startDate);
+        return;
+      }
+      const date = new Date(Number(timestamp) * 1000);
+      this.endDate = this.formatDate(date);
+      const startDate = new Date(date);
+      startDate.setDate(startDate.getDate() - 7);
+      this.startDate = this.formatDate(startDate);
+    },
+    formatDate(date) {
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, "0");
+      const day = String(date.getDate()).padStart(2, "0");
+      return `${year}-${month}-${day}`;
+    },
+    initChart() {
+      const ctx = uni.createCanvasContext("temperatureChart", this);
+      const currentEle = this.eleTabs[this.eleActive];
+      console.log(currentEle, "currentEle");
+      currentEle.data.forEach((item) => {
+        if (item.value === "/") {
+          item.value = "0";
+        }
+      });
+      const series = [
+        {
+          name: currentEle.name,
+          data: currentEle.data,
+          lineWidth: 3,
+          pointShape: "circle",
+          pointSize: 12,
+          pointColor: currentEle.color,
+          pointBorderColor: "#fff",
+          pointBorderWidth: 2,
+          symbol: "emptyCircle",
+          symbolSize: 6,
+          legendShape: "circle",
+        },
+      ];
+      const xData = currentEle.data.map((item) => item.time);
+
+      this.temperatureChart = new uCharts({
+        $this: this,
+        canvasId: "temperatureChart",
+        type: "line",
+        context: ctx,
+        width: 340,
+        height: 180,
+        categories: xData,
+        series: series,
+        legend: {
+          position: "top",
+          show: true,
+          padding: 5,
+          fontColor: "#303133",
+          fontSize: 12,
+          itemType: "circle",
+          itemShape: "emptyCircle",
+          itemWidth: 12,
+          itemHeight: 12,
+          lineWidth: 2,
+        },
+        xAxis: {
+          axisLineColor: "#E4E7ED",
+          disableGrid: true,
+          fontColor: "#656565",
+          fontSize: 10,
+          itemCount: 5,
+          scrollShow: true,
+        },
+        yAxis: {
+          splitNumber: 4,
+          axisLineColor: "#E4E7ED",
+          fontColor: "#656565",
+          gridColor: "#E4E7ED",
+          dashLength: 4,
+          disableGrid: false,
+          fontSize: 10,
+        },
+        dataLabel: false,
+        enableScroll: true,
+        background: "#FFFFFF",
+        extra: {
+          line: {
+            type: "curve",
+            width: 2,
+            activeType: "hollow",
+          },
+        },
+      });
+    },
+    touchLineA(e) {
+      this.temperatureChart.scrollStart(e);
+    },
+    moveLineA(e) {
+      this.temperatureChart.scroll(e);
+    },
+    touchEndLineA(e) {
+      this.temperatureChart.scrollEnd(e);
+      //下面是toolTip事件,如果滚动后不需要显示,可不填写
+      // this.temperatureChart.showToolTip(e, {
+      //   format: function(item, category) {
+      //     console.log(item, 'iyee', e)
+      //     return category + ' ' + item.name + ':' + item.data
+      //   }
+      // });
+    },
+
+    // 打开日期选择器
+    openDatePicker() {
+      this.show = true;
+      // 延迟设置日期,确保组件已经初始化
+      this.$nextTick(() => {
+        if (this.$refs.calendar) {
+          // 直接设置组件内部的日期状态
+          const calendar = this.$refs.calendar;
+          calendar.startDate = this.startDate;
+          calendar.endDate = this.endDate;
+
+          // 如果有选中的日期范围,设置对应的年、月、日
+          if (this.startDate) {
+            const startParts = this.startDate.split("-");
+            calendar.startYear = parseInt(startParts[0]);
+            calendar.startMonth = parseInt(startParts[1]);
+            calendar.startDay = parseInt(startParts[2]);
+          }
+          if (this.endDate) {
+            const endParts = this.endDate.split("-");
+            calendar.endYear = parseInt(endParts[0]);
+            calendar.endMonth = parseInt(endParts[1]);
+            calendar.endDay = parseInt(endParts[2]);
+          }
+
+          // 如果有开始日期,设置日历当前显示的年月为开始日期的年月
+          if (this.startDate) {
+            const startParts = this.startDate.split("-");
+            calendar.year = parseInt(startParts[0]);
+            calendar.month = parseInt(startParts[1]);
+            calendar.changeData();
+          }
+        }
+      });
+    },
+
+    // 日期选择变化事件
+    onDateChange(e) {
+      this.startDate = e.startDate;
+      this.endDate = e.endDate;
+      this.activePeriod = "custom"; // 设置为自定义周期
+      this.show = false;
+      this.getChartData();
+    },
+
+    // 切换时间周期
+    changePeriod(period) {
+      this.activePeriod = period;
+
+      // 根据选择的周期自动计算日期范围
+      const now = new Date();
+      const endDate = now.toISOString().split("T")[0];
+      const startDate = new Date();
+
+      if (period === "24h") {
+        startDate.setDate(now.getDate());
+      } else if (period === "7d") {
+        startDate.setDate(now.getDate() - 7);
+      } else if (period === "30d") {
+        startDate.setMonth(now.getMonth() - 1);
+      }
+
+      this.startDate = startDate.toISOString().split("T")[0];
+      this.endDate = endDate;
+      this.getChartData();
+    },
+
+    // 切换要素标签
+    changeElement(index) {
+      if (this.eleTabs.length === 0) return;
+      this.eleActive = index;
+      this.activeChartInfo = this.eleTabs[index];
+      this.isFolded = true; // 关闭展开面板
+      this.$nextTick(() => {
+        this.initChart();
+      });
+    },
+
+    // 切换要素展开/收起状态
+    toggleFold() {
+      this.isFolded = !this.isFolded;
+    },
+    // 上一页
+    prevPage() {
+      if (this.currentPage > 1) {
+        this.currentPage--;
+        this.getObData();
+      }
+    },
+    // 下一页
+    nextPage() {
+      if (this.currentPage < this.totalPages) {
+        this.currentPage++;
+        this.getObData();
+      }
+    },
+    // 折线图数据
+    getChartData() {
+      this.$myRequest({
+        url: "/api/api_gateway?method=qxz.data.qxz_ob_data_map",
+        data: {
+          device_id: this.deviceInfo.devBid,
+          start: String(
+            Math.floor(new Date(this.startDate + " 00:00:00").getTime() / 1000),
+          ),
+          end: String(
+            Math.floor(new Date(this.endDate + " 23:59:59").getTime() / 1000),
+          ),
+          page: 1,
+          page_size: 999,
+        },
+      }).then((res) => {
+        console.log("getChartData:", res);
+        if (res && Array.isArray(res) && res.length > 0) {
+          this.eleTabs = res;
+          this.hasData = true;
+          this.activeChartInfo =
+            this.eleTabs.find((item) => item.title === this.eleKey) ||
+            this.eleTabs[0];
+          this.eleActive = this.eleTabs.indexOf(this.activeChartInfo);
+          console.log(this.activeChartInfo, "activeChartInfo");
+          this.$nextTick(() => {
+            this.initChart();
+          });
+        } else {
+          this.eleTabs = [];
+          this.hasData = false;
+        }
+      });
+    },
+    // 历史数据
+    getObData() {
+      this.$myRequest({
+        url: "/api/api_gateway?method=qxz.data.qxz_ob_data",
+        data: {
+          device_id: this.deviceInfo.devBid,
+          start: String(
+            Math.floor(new Date(this.startDate + " 00:00:00").getTime() / 1000),
+          ),
+          end: String(
+            Math.floor(new Date(this.endDate + " 23:59:59").getTime() / 1000),
+          ),
+          page: this.currentPage,
+          page_size: this.pageSize,
+        },
+      }).then((res) => {
+        console.log("getObData:", res);
+        const columns = res.title;
+        const data = res.data;
+        const newData = data.map((item) => {
+          return this.processData(item);
+        });
+        const resCol = this.convertDynamic(columns, (prop, val) =>
+          Array.isArray(val) ? `${val[1]}${val[3] || ""}` : val,
+        );
+        this.columns = resCol;
+        this.tableList = newData;
+        this.total = res.count;
+        console.log("tableList:", this.columns);
+        console.log("tableList", this.tableList);
+      });
+    },
+    convertDynamic(data, getLabelValue) {
+      if (!data || typeof data !== "object") return [];
+      const result = [];
+      const keys = Object.keys(data);
+      for (const prop of keys) {
+        const arr = data[prop];
+        const value = getLabelValue(prop, arr, data);
+        if (value !== undefined && value !== null) {
+          result.push({
+            label: String(value),
+            prop,
+            align: "center",
+          });
+        }
+      }
+      return result;
+    },
+    processData(data) {
+      return Object.entries(data).reduce((acc, [key, value]) => {
+        acc[key] = Array.isArray(value) ? value[0] : value;
+        return acc;
+      }, {});
+    },
+  },
+};
+</script>
+
+<style lang="scss">
+.ele-details-page {
+  background:
+    linear-gradient(180deg, #ffffff00 0%, #f5f6fa 23.64%, #f5f6fa 100%),
+    linear-gradient(102deg, #bfeadd 6.77%, #b8f1e7 40.15%, #b9eef5 84.02%);
+  min-height: 100vh;
+  padding: 0 32rpx;
+  padding-top: 98rpx;
+}
+.nav-title{
+  text-align: center;
+}
+.date-picker {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  background-color: #fff;
+  border-radius: 40rpx;
+  padding: 16rpx 24rpx;
+  margin-bottom: 36rpx;
+  font-size: 28rpx;
+  color: #999;
+  margin-top: 46rpx;
+  .date-icon {
+    width: 32rpx;
+    height: 32rpx;
+  }
+}
+
+.separator {
+  margin: 0 16rpx;
+}
+.echarts-board {
+  padding: 32rpx;
+  border-radius: 16rpx;
+  background: #fff;
+  margin-bottom: 24rpx;
+  position: relative;
+  .period-tabs {
+    width: 350rpx;
+    height: 64rpx;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin: auto;
+    margin-bottom: 12rpx;
+    border-radius: 32rpx;
+    background: #f1f4f8;
+    padding: 8rpx 10rpx;
+    color: #303133;
+    font-family: "Source Han Sans CN VF";
+    font-size: 26rpx;
+    font-style: normal;
+    font-weight: 400;
+    line-height: normal;
+    .tab-item {
+      padding: 5rpx 24rpx;
+      height: 48rpx;
+      line-height: 48rpx;
+      border-radius: 32rpx;
+    }
+    .active {
+      background: #fff;
+    }
+  }
+  .element-tabs {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    overflow-x: auto;
+    white-space: nowrap;
+    .ele-board {
+      flex: 1;
+      display: flex;
+      align-items: center;
+      overflow-x: auto;
+      overflow-y: hidden;
+      white-space: nowrap;
+    }
+    .ele-item {
+      position: relative;
+      color: #999999;
+      font-size: 28rpx;
+      font-weight: 400;
+      white-space: nowrap;
+      margin-right: 16rpx;
+      padding: 14rpx 16rpx;
+      cursor: pointer;
+    }
+    .ele-active {
+      color: #303133;
+      font-weight: 500;
+      &::after {
+        content: "";
+        position: absolute;
+        left: 50%;
+        transform: translateX(-50%);
+        bottom: 4rpx;
+        display: block;
+        width: 30rpx;
+        height: 8rpx;
+        border-radius: 4rpx;
+        background: #1fc676;
+      }
+    }
+    .fold-icon {
+      width: 48rpx;
+      border-left: 2px solid #999;
+      padding-left: 16rpx;
+      margin-left: 16rpx;
+      cursor: pointer;
+      display: none;
+    }
+  }
+  /* 展开后的要素容器样式 */
+  .expanded-elements {
+    position: absolute;
+    top: 126rpx;
+    left: 0;
+    right: 0;
+    background: #f5f7fa;
+    border-radius: 12rpx;
+    padding: 16rpx;
+    display: flex;
+    flex-wrap: wrap;
+    gap: 16rpx;
+    z-index: 10;
+    box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
+  }
+
+  .expanded-item {
+    flex: 0 0 calc(33.33% - 16rpx);
+    text-align: center;
+    padding: 16rpx 0;
+    border-radius: 8rpx;
+    color: #999999;
+    font-size: 26rpx;
+    background: #ffffff;
+    cursor: pointer;
+    transition: all 0.3s ease;
+  }
+
+  .expanded-item.ele-active {
+    color: #1fc676;
+    background: #e6f7ef;
+    font-weight: 500;
+  }
+
+  .chart-container {
+    border-radius: 16rpx;
+    margin-bottom: 24rpx;
+    position: relative;
+    padding: 10rpx;
+    overflow: hidden;
+    height: 336rpx;
+    z-index:0;
+    .chart {
+      width: 100%;
+      height: 336rpx;
+    }
+  }
+
+  .no-data {
+    border-radius: 16rpx;
+    margin-bottom: 24rpx;
+    padding: 10rpx;
+    height: 336rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background-color: #f9f9f9;
+    text {
+      color: #999;
+      font-size: 32rpx;
+    }
+  }
+
+  .stats-panel {
+    display: flex;
+    justify-content: space-between;
+    background-color: #f1f4f8;
+    border-radius: 16rpx;
+    padding: 24rpx 64rpx;
+    .stat-item {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      .label {
+        color: #656565;
+        font-size: 24rpx;
+        font-weight: 400;
+      }
+      .value {
+        font-size: 40rpx;
+        font-weight: bold;
+      }
+      .unit {
+        color: #999999;
+        font-size: 20rpx;
+      }
+      .red {
+        color: #fb4e52;
+      }
+    }
+  }
+}
+
+.history-section {
+  background-color: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx 32rpx;
+  .section-title {
+    font-size: 28rpx;
+    font-weight: bold;
+    margin-bottom: 16rpx;
+    color: #303133;
+  }
+  .table-wrap {
+    display: flex;
+    width: 100%;
+    box-sizing: border-box;
+    margin: 20rpx 0;
+    color: #042118;
+    font-family: "Source Han Sans CN VF";
+    font-size: 24rpx;
+    font-style: normal;
+    font-weight: 400;
+    line-height: normal;
+    .fixed-column {
+      width: 250rpx;
+      .table-cell {
+        width: 100%;
+        white-space: nowrap;
+      }
+    }
+    .table-bg {
+      width: 16rpx;
+      background: linear-gradient(270deg, #ffffff33 0%, #9598a433 100%);
+    }
+    .scroll-column {
+      flex: 1;
+      height: auto;
+      overflow: hidden;
+      margin-left: -10rpx;
+    }
+    .scroll-content {
+      min-width: 100%;
+      width: fit-content;
+      display: flex;
+      flex-direction: column;
+      .table-row {
+        display: flex;
+        .table-cell {
+          flex: 1;
+        }
+      }
+    }
+    .table-cell {
+      width: 160rpx;
+      height: 64rpx;
+      line-height: 64rpx;
+      text-align: center;
+      border-bottom: 2px solid #e4e7ed;
+      box-sizing: border-box;
+    }
+    .table-cell.header {
+      background: #f6f8fc;
+    }
+  }
+
+  .pagination {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-top: 24rpx;
+    .page-item {
+      width: 104rpx;
+      height: 48rpx;
+      color: #656565;
+      font-size: 24rpx;
+      line-height: 48rpx;
+      text-align: center;
+      border: 2rpx solid #e4e7ed;
+      border-radius: 8rpx;
+    }
+    .page-item.disabled {
+      color: #999;
+      border-color: #e4e7ed;
+      background-color: #f5f7fa;
+    }
+    .next {
+      color: #0bbc58;
+      border: 2rpx solid #0bbc58;
+    }
+    .next.disabled {
+      color: #999;
+      border-color: #e4e7ed;
+      background-color: #f5f7fa;
+    }
+    .page-info {
+      color: #999999;
+      font-family: "Source Han Sans CN VF";
+      font-size: 24rpx;
+      font-style: normal;
+      font-weight: 400;
+      line-height: normal;
+      .curret-page {
+        color: #303133;
+      }
+    }
+  }
+}
+::v-deep .u-calendar__action {
+  display: flex;
+  justify-content: center;
+}
+</style>

+ 991 - 0
pages/deviceDetails/weatherStation1/index.vue

@@ -0,0 +1,991 @@
+<template>
+  <view class="device-details-page">
+    <cu-custom :isBack="true">
+      <template slot="content">
+        <view class="nav-title">{{ deviceInfo.devName }}</view>
+      </template>
+    </cu-custom>
+
+    <!-- 设备基本信息 -->
+    <view class="device-info">
+      <view class="operation" @click.stop="isShowOperation = !isShowOperation">操作</view>
+      <view class="operation-container" v-if="isShowOperation" @click="closeOperationHandler">
+      <view class="operation-background"></view>
+      <view class="operation-content">
+        <view class="operation-item" @click="goPage('devRepair')">
+          <image :src="serviceIcon" class="operation-icon"></image>
+          维修
+        </view>
+        <view class="operation-item" @click="goPage('devControl')">
+          <image :src="settingIcon" class="operation-icon"></image>
+          设置
+        </view>
+        <view class="operation-item" @click="goPage('simDetail')">
+          <image :src="simIcon" class="operation-icon"></image>
+          SIM卡
+        </view>
+        <!-- <view class="operation-item" @click="modification">
+          <image :src="editIcon" class="operation-icon"></image>
+          修改
+        </view> -->
+      </view>
+    </view>
+      <view @click="copy(deviceInfo.devBid)" class="info-item">
+        <text class="info-label">设备ID</text>
+        <text class="info-value">{{ deviceInfo.devBid }}</text>
+        <image
+          src="/static/images/device/copy.svg"
+          mode=""
+          class="info-img"
+        ></image>
+      </view>
+      <view class="info-item">
+        <text class="info-label">上报时间</text>
+        <text class="info-value">{{ formatTimestamp(deviceInfo.uptime) }}</text>
+      </view> 
+      <view class="info-item">
+        <text class="info-label">设备位置</text>
+        <text class="info-value">{{ deviceInfo.address }}</text>
+      </view>
+      <view class="dev-status">{{
+        deviceInfo.devStatus == '1' ? '在线' : '离线'
+      }}</view>
+    </view>
+
+    <!-- 标签页切换 -->
+    <view class="tab-container">
+      <view class="tab-top">
+        <view
+          class="tab-item"
+          :class="{ active: activeTab === 'monitoring' }"
+          @click="switchTab('monitoring')"
+        >
+          <text class="tab-text">监测要素</text>
+          <image
+            v-if="activeTab === 'monitoring'"
+            src="/static/images/device/smile.svg"
+            mode=""
+            class="smile"
+          ></image>
+        </view>
+        <view
+          class="tab-item"
+          :class="{ active: activeTab === 'details' }"
+          @click="switchTab('details')"
+        >
+          <text class="tab-text">设备数据</text>
+          <image
+            v-if="activeTab === 'details'"
+            src="/static/images/device/smile.svg"
+            mode=""
+            class="smile"
+          ></image>
+        </view>
+      </view>
+    </view>
+
+    <!-- 标签页内容 -->
+    <view class="tab-content">
+      <!-- 监测要素页面 -->
+      <view v-if="activeTab === 'monitoring'" class="monitoring-page">
+        <view
+          class="data-card"
+          v-for="(item, index) in monitorList"
+          :key="index"
+          @click="goPage('eleDetail', item)"
+        >
+          <view class="card-title">{{ item.name }}</view>
+          <view class="data-value-row">
+            <view>
+              <text v-if="item.value && item.value.length" class="data-value">{{
+                item.value
+              }}</text>
+              <text class="data-unit">{{ item.unit }}</text>
+            </view>
+            <image
+              class="device-img"
+              :src="`/static/images/device/${item.key}-default.svg`"
+              mode="aspectFit"
+            ></image>
+          </view>
+        </view>
+      </view>
+
+      <!-- 设备详情页面 -->
+      <view v-if="activeTab === 'details'" class="details-page">
+      <view class="device-params">
+          <view class="device-control">
+            <view class="control-left">
+              <view>{{formatTimestamp(deviceInfo.uptime)}}</view>
+              <u-icon name="reload" color="#0BBC58" style="margin-left: 12rpx" @click="refreshData"></u-icon>
+            </view>
+          </view>
+          <view class="params-board">
+            <view class="params-left">
+              <view
+                v-for="(item, index) in statusList.slice(0, 4)"
+                :key="index"
+                class="params-item"
+              >
+                <view class="params-val">
+                  {{ item.value }}
+                  <span class="unit">{{ item.unit }}</span>
+                </view>
+                <view class="params-label">{{ item.name }}</view>
+              </view>
+            </view><view class="params-left">
+              <view
+                v-for="(item, index) in statusList.slice(4, 8)"
+                :key="index"
+                class="params-item"
+              >
+                <view class="params-val">
+                  {{ item.value }}
+                  <span class="unit">{{ item.unit }}</span>
+                </view>
+                <view class="params-label">{{ item.name }}</view>
+              </view>
+            </view>
+            <view class="params-right">
+              <image
+                class="device-img"
+                style="width: 150rpx; height: 300rpx;"
+                src="/static/images/device/qxz_low.png"
+                mode="aspectFit"
+              ></image>
+            </view>
+          </view>
+        </view>
+        <!-- 设备数据表格 -->
+        <view class="table-container">
+          <view class="table-control">
+            <view class="title">设备数据</view>
+            <view class="date-board" @click="openDatePicker">
+              <text>{{ startDate }}</text>
+              <text class="line">至</text>
+              <text>{{ endDate }}</text>
+              <image class="date-icon" src="/static/images/device/date.svg" />
+            </view>
+          </view>
+          <u-calendar
+            v-model="show"
+            :mode="mode"
+            @change="onDateChange"
+            range-color="#999" btn-type="success" active-bg-color="#0BBC58" range-bg-color="rgba(11,188,88,0.13)"
+            ref="calendar"
+          ></u-calendar>
+          <view class="table-wrap" v-if="tableData && tableData.length > 0">
+            <view class="fixed-column">
+              <view class="table-cell header">上报时间</view>
+              <view
+                class="table-cell"
+                v-for="(item, idx) in tableData"
+                :key="idx"
+              >
+                {{ item.time }}
+              </view>
+            </view>
+            <view class="table-bg"></view>
+            <scroll-view class="scroll-column" scroll-x="true">
+              <view class="scroll-content">
+                <view class="table-row">
+                  <view
+                    class="table-cell header"
+                    v-for="(item, index) in tableColumn"
+                    :key="index"
+                    >{{ item.label }}</view
+                  >
+                </view>
+                <view
+                  class="table-row"
+                  v-for="(item, idx) in tableData"
+                  :key="idx"
+                >
+                  <view
+                    class="table-cell"
+                    v-for="(item1, index1) in tableColumn"
+                    :key="index1"
+                    >{{ item[item1.prop] }}</view
+                  >
+                </view>
+              </view>
+            </scroll-view>
+          </view>
+          <view class="empty-state" v-else>
+            <u-empty text="暂无数据" mode="list"></u-empty>
+          </view>
+
+          <view class="pagination" v-if="tableData && tableData.length > 0">
+            <view
+              class="page-item prev"
+              :class="{ disabled: page <= 1 }"
+              @click="prevPage"
+              >上一页</view
+            >
+            <view class="page-info">
+              <text class="curret-page">{{ page }}</text>
+              <text>/</text>
+              <text>{{ totalPages }}</text>
+            </view>
+            <view
+              class="page-item next"
+              :class="{ disabled: page >= totalPages }"
+              @click="nextPage"
+              >下一页</view
+            >
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  onLoad(options) {
+    this.deviceInfo = options;
+    this.initDateRange(options.uptime);
+    this.getQxzObData();
+    // this.getDeviceStatus();
+    this.getDeviceData();
+  },
+  data() {
+    return {
+      deviceInfo: {},
+      isShowOperation: false,
+      activeTab: 'monitoring',
+      monitorList: [],
+      statusList: [],
+      show: false,
+      mode: 'range',
+      startDate: '',
+      endDate: '',
+      page: 1,
+      photoIcon:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/photoIcon.png',
+      editIcon:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/editIcon.png',
+      serviceIcon:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/serviceIcon.png',
+      simIcon:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/simIcon.png',
+      settingIcon:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/settingIcon.png',
+      isShowPhoto:false,
+      pageSize: 9,
+      total: 0,
+      tableData: [],
+      tableColumn: [],
+    };
+  },
+  computed: {
+    totalPages() {
+      return Math.ceil(this.total / this.pageSize) || 1;
+    },
+  },
+  methods: {
+    closeOperationHandler(){
+      this.isShowOperation = false;
+    },
+    async refreshData(){
+      //api/api_gateway?method=qxz.device.control
+      await this.$myRequest({
+        url: '/api/api_gateway?method=qxz.device.control',
+        data: {
+          device_id: this.deviceInfo.devBid,
+          cmd: 'READ',
+        },
+      });
+      //弹框提示
+      uni.showToast({
+        title: '刷新成功',
+        icon: 'success',
+      });
+      this.getDeviceData();
+    },
+    formatTimestamp(timestamp) {
+      if (!timestamp) return '';
+      const date = new Date(Number(timestamp) * 1000);
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      const hours = String(date.getHours()).padStart(2, '0');
+      const minutes = String(date.getMinutes()).padStart(2, '0');
+      const seconds = String(date.getSeconds()).padStart(2, '0');
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
+    formatDate(date) {
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      return `${year}-${month}-${day}`;
+    },
+    initDateRange(timestamp) {
+      if (!timestamp) {
+        const today = new Date();
+        this.endDate = this.formatDate(today);
+        const startDate = new Date(today);
+        startDate.setDate(startDate.getDate() - 7);
+        this.startDate = this.formatDate(startDate);
+        return;
+      }
+      const date = new Date(Number(timestamp) * 1000);
+      this.endDate = this.formatDate(date);
+      const startDate = new Date(date);
+      startDate.setDate(startDate.getDate() - 7);
+      this.startDate = this.formatDate(startDate);
+    },
+    switchTab(tab) {
+      this.activeTab = tab;
+    },
+    copy(item) {
+      uni.setClipboardData({
+        data: item,
+        success: function () {
+        },
+      });
+    },
+    goPage(type, eleItem) {
+      let url = '';
+      console.log(type,eleItem,'deviceInfodeviceInfo')
+      if (eleItem && eleItem.key) {
+        url = `/pages/deviceDetails/weatherStation1/${type}?deviceInfo=${encodeURIComponent(JSON.stringify(this.deviceInfo))}&eleKey=${eleItem.key}`;
+      } else {
+        url = `/pages/deviceDetails/weatherStation1/${type}?deviceInfo=${encodeURIComponent(JSON.stringify(this.deviceInfo))}`;
+      }
+
+      if (type === 'devRepair') {
+        url = `/pages/afterSale/addafter?d_id=${this.deviceInfo.d_id}&device_id=${this.deviceInfo.devBid}&device_type=${this.deviceInfo.deviceType}`;
+      }
+
+      uni.navigateTo({
+        url,
+      });
+    },
+    openDatePicker() {
+      this.show = true;
+    },
+    onDateChange(event) {
+      if (event && event.startDate && event.endDate) {
+        this.startDate = event.startDate;
+        this.endDate = event.endDate;
+        this.show = false;
+        this.page = 1;
+        this.getDeviceData();
+      }
+    },
+    prevPage() {
+      if (this.page <= 1) return;
+      this.page--;
+      this.getDeviceData();
+    },
+    nextPage() {
+      if (this.page >= this.totalPages) return;
+      this.page++;
+      this.getDeviceData();
+    },
+    // 获取设备最新监测数据
+    async getQxzObData() {
+      const res = await this.$myRequest({
+        url: '/api/api_gateway?method=greenhouse.env_first.recent_data',
+        data: {
+          device_id: this.deviceInfo.devBid,
+        },
+      });
+      const recent = res.recent || [];
+      this.statusList = recent;
+      // recent.forEach(item => {
+      //   this.statusList.push({
+      //     label: item.name,
+      //     val: item.value,
+      //     key: item.name,
+      //   })
+      // })
+      this.monitorList = this.formatChartData(res.recent);
+    },
+    formatChartData(data) {
+      data.forEach(item => {
+        if(item.name == '空气温度'){
+          item.key = 'TEMPA';
+        }else if(item.name == '空气湿度'){
+          item.key = 'HUMIA';
+        }else if(item.name == '风速'){
+          item.key = 'PRESA';
+        }else if(item.name == '风向'){
+          item.key = 'WDIRA';
+        }else if(item.name == '光照度'){
+          item.key = 'ILLUA';
+        }else if(item.name == '降雨量累计'){
+          item.key = 'PRECA';
+        }else{
+          item.key = 'TEMPA';
+        }
+      })
+      return data;
+    },
+    convertDynamic(data, getLabelValue) {
+      if (!data || typeof data !== 'object') return [];
+      const result = [];
+      const keys = Object.keys(data);
+      for (const prop of keys) {
+        const arr = data[prop];
+        const value = getLabelValue(prop, arr, data);
+        if (value !== undefined && value !== null) {
+          result.push({
+            label: String(value),
+            prop,
+            align: 'center',
+          });
+        }
+      }
+      return result;
+    },
+    // 获取设备状态数据
+    async getDeviceStatus() {
+      const res = await this.$myRequest({
+        url: '/api/api_gateway?method=qxz.data.qxz_status',
+        data: {
+          device_id: this.deviceInfo.devBid,
+        },
+      });
+      const newArr = this.transformObjectToArray(res);
+      this.statusList = newArr;
+    },
+    transformObjectToArray(originalData) {
+      if (
+        typeof originalData !== 'object' ||
+        originalData === null ||
+        Array.isArray(originalData)
+      ) {
+        return [];
+      }
+
+      return Object.keys(originalData).map((key) => {
+        const [val = '', label = ''] = originalData[key] || [];
+        return {
+          label,
+          val,
+          key,
+        };
+      });
+    },
+    // 设备数据列表
+    async getDeviceData() {
+      const data = await this.$myRequest({
+        url: '/api/api_gateway?method=greenhouse.env_first.history_data',
+        data: {
+          device_id: this.deviceInfo.devBid,
+          page: String(this.page),
+          page_size: String(this.pageSize),
+          start: String(
+            Math.floor(new Date(this.startDate + ' 00:00:00').getTime() / 1000),
+          ),
+          end: String(
+            Math.floor(new Date(this.endDate + ' 23:59:59').getTime() / 1000),
+          ),
+        },
+      });
+      this.total = data?.total || 0;
+      this.tableData = data.table || [];
+      const title = data.title;
+      this.tableColumn = title
+        .map((item) => {
+          return {
+            label: item.name,
+            prop: item.key,
+          };
+        })
+        .filter((item) => item.key !== 'time');
+    },
+  },
+};
+</script>
+
+<style lang="scss">
+.device-details-page {
+  background:
+    linear-gradient(180deg, #ffffff00 0%, #f5f6fa 23.64%, #f5f6fa 100%),
+    linear-gradient(102deg, #bfeadd 6.77%, #b8f1e7 40.15%, #b9eef5 84.02%);
+  min-height: 100vh;
+  padding: 0 32rpx;
+  padding-top: 98rpx;
+}
+.nav-title{
+  //超出隐藏
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  text-align: center;
+}
+/* 设备基本信息 */
+.device-info {
+  background-color: #ffffff;
+  margin: 46rpx 0 24rpx 0;
+  padding: 32rpx 24rpx;
+  border-radius: 16rpx;
+  background: linear-gradient(180deg, #eff 0%, #fff 23.56%);
+  position: relative;
+  .info-item {
+    display: flex;
+    align-items: center;
+    margin-bottom: 16rpx;
+  }
+  .info-item:last-child {
+    margin-bottom: 0;
+  }
+
+
+.operation-container{
+  position: fixed;
+  right: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 99;
+}
+.operation{
+  position: fixed;
+  top: 260rpx;
+  right: 0;
+  z-index: 999;
+  width:48rpx;
+  height: 100rpx;
+  line-height: 50rpx;
+  border-radius: 8px 0 0 8px;
+  border-top: 4rpx solid #FFF;
+  border-bottom: 4rpx solid #FFF;
+  border-left: 4rpx solid #FFF;
+  background: #dddfe6a3;
+  color: #515153;
+  text-align: center;
+  font-family: "Source Han Sans CN VF";
+  font-size: 24rpx;
+  font-weight: 500;
+  writing-mode: vertical-rl;
+}
+.operation-background{
+  position: fixed;
+  right: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 998;
+  background: #00000040;
+}
+.operation-content{
+  position: fixed;
+  top: 240rpx;
+  right: 80rpx;
+  height: 126rpx;
+  display: flex;
+  padding: 16rpx 32rpx;
+  align-items: center;
+  gap: 64rpx;
+  z-index: 999;
+  border-radius: 32rpx;
+  border: 2rpx solid #FFF;
+  background: #FFF;
+  backdrop-filter: blur(8rpx);
+  .operation-item{
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    color: #333333;
+    text-align: center;
+    font-family: "Source Han Sans CN VF";
+    font-size: 24rpx;
+    font-weight: 400;
+  }
+  .operation-icon{
+    width: 58rpx;
+    height: 58rpx;
+  }
+}
+  .info-label {
+    width: 116rpx;
+    color: #999999;
+    text-align: right;
+    font-family: 'Source Han Sans CN VF';
+    font-size: 28rpx;
+    font-style: normal;
+    font-weight: 400;
+    line-height: normal;
+    margin-right: 32rpx;
+    white-space: nowrap;
+  }
+
+  .info-value {
+    color: #333333;
+    font-family: 'Source Han Sans CN VF';
+    font-size: 28rpx;
+    font-style: normal;
+    font-weight: 400;
+    line-height: normal;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+  }
+  .info-img {
+    width: 32rpx;
+    height: 32rpx;
+    margin-left: 16rpx;
+    background: #0bbc581a;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  .tishi {
+    width: 24rpx;
+    height: 24rpx;
+  }
+  .dev-status {
+    position: absolute;
+    right: 0;
+    top: 0;
+    border: 2rpx solid #ffffff;
+    background: #0bbc581a;
+    width: 152rpx;
+    height: 56rpx;
+    color: #0bbc58;
+    text-align: center;
+    font-size: 28rpx;
+    font-weight: 400;
+    line-height: 52rpx;
+    border-radius: 0 16rpx 0 64rpx;
+  }
+}
+
+/* 标签页切换 */
+.tab-container {
+  background-color: #ffffff;
+  border-radius: 16rpx;
+  overflow: hidden;
+  padding: 0 32rpx;
+  margin-bottom: 24rpx;
+  .tab-top {
+    display: flex;
+  }
+  .tab-item {
+    flex: 1;
+    height: 80rpx;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    position: relative;
+    color: #666666;
+    text-align: center;
+    font-family: 'Source Han Sans CN VF';
+    font-size: 28rpx;
+    font-style: normal;
+    font-weight: 400;
+    line-height: normal;
+    .tab-text {
+      margin-top: 16rpx;
+    }
+    .smile {
+      width: 24rpx;
+      height: 18rpx;
+    }
+  }
+
+  .tab-item.active {
+    color: #333333;
+    font-weight: 700;
+  }
+
+  .time-board {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-bottom: 16rpx;
+  }
+  .time-filter {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    width: 256rpx;
+    height: 48rpx;
+    padding: 6rpx 8rpx;
+    border-radius: 32rpx;
+    background: #f1f4f8;
+    color: #303133;
+    font-size: 20rpx;
+    font-weight: 400;
+    .filter-item {
+      padding: 4rpx 16rpx;
+      height: 36rpx;
+      line-height: 36rpx;
+    }
+    .active {
+      border-radius: 32rpx;
+      background: #fff;
+    }
+  }
+}
+
+/* 监测要素页面 */
+.monitoring-page {
+  padding-bottom: 20px;
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  .data-card {
+    width: calc(50% - 8rpx);
+    margin-bottom: 16rpx;
+    background-color: #ffffff;
+    border-radius: 16rpx;
+    padding: 16rpx 24rpx;
+    height: 186rpx;
+    font-family: 'Source Han Sans CN VF';
+    font-size: 28rpx;
+    font-weight: 400;
+    box-sizing: border-box;
+    .card-title {
+      color: #042118;
+      margin-bottom: 6rpx;
+    }
+    .data-value-row {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      color: #042118;
+      font-family: 'Source Han Sans CN VF';
+      font-weight: 400;
+      .data-value {
+        font-size: 40rpx;
+        font-weight: 700;
+        margin-right: 8rpx;
+      }
+      .data-unit {
+        font-size: 24rpx;
+      }
+      .device-img {
+        width: 52rpx;
+        height: 52rpx;
+      }
+    }
+    .card-thrid {
+      color: #687a74;
+      margin-top: 6rpx;
+    }
+  }
+}
+/* 设备详情页面 */
+.details-page {
+  .device-params {
+    background-color: #ffffff;
+    border-radius: 16rpx;
+    padding: 32rpx;
+    margin-bottom: 24rpx;
+    .device-control {
+      position: relative;
+      z-index: 3;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      margin-bottom: 24rpx;
+      .control-left {
+        color: #999999;
+        font-family: 'Source Han Sans CN VF';
+        font-size: 28rpx;
+        display: flex;
+        align-items: center;
+        .reload-img {
+          font-size: 32rpx;
+          color: #0bbc58;
+          margin-left: 14rpx;
+        }
+      }
+      .control-right {
+        display: flex;
+        align-items: center;
+        .control-img {
+          width: 48rpx;
+          height: 48rpx;
+          margin-left: 24rpx;
+        }
+      }
+    }
+    .params-board {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      position: relative;
+      .params-left {
+        display: flex;
+        flex-wrap: wrap;
+        .params-item {
+          display: flex;
+          flex-direction: column;
+          width: 100%;
+          // width: calc(50% - 20rpx);
+          margin-bottom: 24rpx;
+          .params-val {
+            color: #303133;
+            font-size: 28rpx;
+            font-weight: 700;
+            .unit {
+              font-size: 24rpx;
+              font-weight: 400;
+              color:#999999;
+            }
+          }
+          .params-label {
+            color: #999999;
+            font-size: 24rpx;
+            font-weight: 400;
+          }
+        }
+      }
+      .params-right {
+        position: absolute;
+        right: 0;
+        top:0;
+        .device-img {
+          width: 150rpx;
+          height: 300rpx;
+        }
+      }
+    }
+  }
+  .table-container {
+    padding: 16rpx 32rpx 32rpx 32rpx;
+    border-radius: 16rpx;
+    background: #fff;
+    .table-control {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      margin-bottom: 16rpx;
+      .title {
+        color: #042118;
+        font-family: 'Source Han Sans CN VF';
+        font-size: 28rpx;
+        font-style: normal;
+        font-weight: 700;
+        line-height: normal;
+      }
+      .date-board {
+        display: flex;
+        align-items: center;
+        color: #020305;
+        font-family: 'Source Han Sans CN VF';
+        font-size: 24rpx;
+        font-weight: 400;
+        border-radius: 32rpx;
+        background: #f1f4f8;
+        padding: 16rpx 24rpx;
+        .line {
+          color: #656565;
+          margin: 0 10rpx;
+        }
+        .date-icon {
+          width: 32rpx;
+          height: 32rpx;
+          margin-left: 16rpx;
+        }
+      }
+    }
+    .table-wrap {
+      display: flex;
+      width: 100%;
+      height: 850rpx;
+      box-sizing: border-box;
+      margin: 20rpx 0;
+      color: #042118;
+      font-family: 'Source Han Sans CN VF';
+      font-size: 24rpx;
+      font-style: normal;
+      font-weight: 400;
+      line-height: normal;
+      .fixed-column {
+        width: 250rpx;
+        .table-cell {
+          width: 100%;
+          white-space: nowrap;
+        }
+      }
+      .table-bg {
+        width: 16rpx;
+        background: linear-gradient(270deg, #ffffff33 0%, #9598a433 100%);
+      }
+      .scroll-column {
+        flex: 1;
+        height: auto;
+        overflow: hidden;
+        margin-left: -10rpx;
+      }
+      .scroll-content {
+        min-width: 100%;
+        width: fit-content;
+        display: flex;
+        flex-direction: column;
+        .table-row {
+          display: flex;
+          .table-cell {
+            flex: 1;
+          }
+        }
+      }
+      .table-cell {
+        width: 160rpx;
+        height: 80rpx;
+        line-height: 80rpx;
+        text-align: center;
+        border-bottom: 2px solid #e4e7ed;
+        box-sizing: border-box;
+        overflow: hidden;
+        padding: 0 4rpx;
+        text-overflow: ellipsis;
+      }
+      .table-cell.header {
+        background: #f6f8fc;
+      }
+    }
+
+    .pagination {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      margin-top: 24rpx;
+      .page-item {
+        width: 104rpx;
+        height: 48rpx;
+        color: #656565;
+        font-size: 24rpx;
+        line-height: 48rpx;
+        text-align: center;
+        border: 2rpx solid #e4e7ed;
+        border-radius: 8rpx;
+      }
+      .prev.disabled {
+        color: #c0c4cc;
+        border-color: #e4e7ed;
+      }
+      .next {
+        color: #0bbc58;
+        border: 2px solid #0bbc58;
+      }
+      .next.disabled {
+        color: #c0c4cc;
+        border-color: #e4e7ed;
+      }
+      .page-info {
+        color: #999999;
+        font-family: 'Source Han Sans CN VF';
+        font-size: 24rpx;
+        font-style: normal;
+        font-weight: 400;
+        line-height: normal;
+        .curret-page {
+          color: #303133;
+        }
+      }
+    }
+
+    .empty-state {
+      padding: 80rpx 0;
+    }
+  }
+}
+::v-deep .u-calendar__action {
+  display: flex;
+  justify-content: center;
+}
+</style>

+ 345 - 0
pages/deviceDetails/weatherStation1/simDetail.vue

@@ -0,0 +1,345 @@
+<template>
+  <view class="sim-details-page">
+    <cu-custom :isBack="true">
+      <template slot="content">
+        <view class="nav-title">SIM卡详情</view>
+      </template>
+    </cu-custom>
+    <!-- 加载动画 -->
+    <view v-if="loading" class="loading-container">
+      <u-loading mode="circle"></u-loading>
+      <text class="loading-text">加载中...</text>
+    </view>
+    <!-- 页面内容 -->
+    <view v-else>
+      <view class="sim-first sim-board">
+        <view class="title">数据SIM卡流量</view>
+        <view class="sim-item">
+          <text class="label">ICCID:</text>
+          <span class="val">{{ simData.iccid }}</span>
+          <image
+            @click="copy(simData.iccid)"
+            src="/static/images/device/copy.svg"
+            mode=""
+            class="info-img"
+          ></image>
+        </view>
+        <view class="sim-item">
+          <text class="label">总流量:</text>
+          <span class="val">{{ simData.total }}M</span>
+        </view>
+        <view class="sim-item">
+          <text class="label">状态:</text>
+          <text class="val">{{ simData.status }}</text>
+        </view>
+        <view class="sim-item">
+          <text class="label">到期时间:</text>
+          <text class="val">{{ simData.expire }}</text>
+        </view>
+        <view class="sim-item">
+          <text class="label">厂商名称:</text>
+          <text class="val">{{ simData.company }}</text>
+        </view>
+        <view class="sim-info-count">
+          <view class="sim-item">
+            <text class="label">已使用:</text
+            ><text
+              :style="{
+                color:
+                  simData.used > parseInt(simData.total) ? '#f56c6c' : '#0bbc58',
+              }"
+              >{{ simData.used.toFixed(2) }}M</text
+            >/{{ simData.total }}M
+          </view>
+          <view class="sim-item">
+            <text class="label">剩余:</text>
+            <text
+              class="val"
+              :style="{
+                color: '#0bbc58',
+              }"
+              >{{ (parseInt(simData.total || 0) - simData.used).toFixed(2) }}M</text
+            >
+          </view>
+        </view>
+      </view>
+      <view v-if="simIccidObj.photo_iccid || simIccidObj.is_editor" class="sim-board sim-second">
+        <view class="title">图片SIM卡流量</view>
+        <view class="sim-item">
+          <text class="label">ICCID:</text>
+          <span v-if="!isEditing" class="val">{{ simData.iccid }}</span>
+          <input
+            v-else
+            v-model="editingIccid"
+            type="text"
+            class="edit-input"
+            placeholder="请输入新的ICCID"
+          />
+          <image
+            @click="copy(isEditing ? editingIccid : simData.iccid)"
+            src="/static/images/device/copy.svg"
+            mode=""
+            class="info-img"
+          ></image>
+          <image
+            v-if="!isEditing"
+            @click="toggleEdit"
+            src="/static/images/device/edit.svg"
+            mode=""
+            class="info-img"
+          ></image>
+          <button v-else @click="saveEditIccid" class="save-btn">确定</button>
+        </view>
+      </view>
+      <view class="footer-board">
+        <u-button type="success" class="sub-btn" @click="goBack"
+          >确定</u-button
+        >
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  onLoad(options) {
+    this.loading = true
+    const deviceInfo = JSON.parse(decodeURIComponent(options.deviceInfo));
+    console.log(deviceInfo, "deviceInfo");
+    this.deviceInfo = deviceInfo;
+    this.getIccidByd_id();
+  },
+  data() {
+    return {
+      deviceInfo: {},
+      simData: {
+        iccid: "",
+        total: "",
+        status: "",
+        expire: "",
+        company: "",
+        used: 0,
+      },
+      isEditing: false,
+      editingIccid: "",
+      showSimData: false,
+      empty: false,
+      simIccidObj: {},
+      loading: false,
+    };
+  },
+  methods: {
+    getIccidByd_id() {
+      this.$myRequest({
+        url: "/api/api_gateway?method=forecast.send_control.device_sim_new",
+        data: {
+          d_id: this.deviceInfo.d_id,
+        },
+      }).then((res) => {
+        console.log('getIccidByd_id:', res);
+        if (
+          res.data_iccid ||
+          res.photo_iccid ||
+          res.is_editor
+        ) {
+          console.log(res, '1111')
+          this.empty = false;
+          this.simIccidObj = res;
+          if (res.data_iccid) {
+            this.active = "data_iccid";
+            console.log(222)
+            this.getSimData(res.data_iccid);
+          } else {
+            if (res.photo_iccid || res.is_editor) {
+              this.active = "photo_iccid";
+              if (res.photo_iccid) {
+                this.getSimData(res.photo_iccid);
+              } else {
+                this.loading = false;
+              }
+            }
+          }
+        } else {
+          this.empty = true;
+          this.loading = false;
+        }
+      });
+    },
+    getSimData(iccid) {
+      this.$myRequest({
+        url: "/api/api_gateway?method=forecast.send_control.sim_query_new",
+        data: {
+          iccid: iccid
+        }
+      }).then(res => {
+        console.log('simData:', res)
+        this.loading = false
+        this.simData = res
+        if (res.code) {
+          this.showSimData = false
+          // this.$message.error(res.data.data.msg)
+        } else {
+          this.showSimData = true
+          this.simData = res
+          this.editIccid = res.iccid
+        }
+      })
+    },
+    copy(item) {
+      console.log(item);
+      uni.setClipboardData({
+        data: item,
+        success: function () {
+          console.log("success");
+        },
+      });
+    },
+    toggleEdit() {
+      this.isEditing = !this.isEditing;
+      if (this.isEditing) {
+        this.editingIccid = this.simData.iccid;
+      }
+    },
+    saveIccid() {
+      this.simData.iccid = this.editingIccid;
+      this.isEditing = false;
+    },
+    saveEditIccid() {
+      this.$myRequest({
+        url: '/api/api_gateway?method=forecast.send_control.device_sim_update',
+        data: {
+          d_id: this.deviceInfo.d_id,
+          simid: this.editingIccid
+        }
+      }).then(res => {
+        if (res.code) {
+          this.isEditSim = false
+          this.loading = true
+          // this.$message.success(res.data.data.msg)
+          this.simIccidObj.photo_iccid = this.editingIccid
+          this.getSimData(this.editingIccid)
+        } else {
+          // this.$message.error(res.data.data.msg)
+        }
+        this.isEditing = false;
+      })
+    },
+    goBack() {
+      uni.navigateBack({
+        delta: 1,
+      })
+    },
+  },
+};
+</script>
+
+<style lang="scss">
+.sim-details-page {
+  background:
+    linear-gradient(180deg, #ffffff00 0%, #f5f6fa 23.64%, #f5f6fa 100%),
+    linear-gradient(102deg, #bfeadd 6.77%, #b8f1e7 40.15%, #b9eef5 84.02%);
+  min-height: 100vh;
+  padding: 0 32rpx;
+  padding-top: 98rpx;
+  box-sizing: border-box;
+}
+/* 加载动画样式 */
+.loading-container {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 60vh;
+  width: 100%;
+}
+.loading-text {
+  font-size: 28rpx;
+  color: #666;
+  margin-top: 32rpx;
+}
+.nav-title{
+  text-align: center;
+}
+.sim-board {
+  padding: 32rpx;
+  border-radius: 16rpx;
+  background: #fff;
+  margin-top: 46rpx;
+  margin-bottom: 24rpx;
+  .title {
+    color: #020305;
+    font-size: 32rpx;
+    font-weight: 700;
+    margin-bottom: 24rpx;
+  }
+  .sim-item {
+    font-size: 28rpx;
+    display: flex;
+    align-items: center;
+    margin-bottom: 24rpx;
+    .label {
+      color: #999999;
+    }
+    .val {
+      color: #303133;
+    }
+    .copy-icon,
+    .edit-icon {
+      width: 32rpx;
+      height: 32rpx;
+    }
+  }
+  .sim-info-count {
+    display: flex;
+    justify-content: space-between;
+  }
+}
+.footer-board {
+  border-top: 2rpx solid #e4e7ed;
+  background: #fff;
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 164rpx;
+  padding: 16rpx 32rpx 0 32rpx;
+  .sub-btn {
+    width: 100%;
+    background: #0bbc58;
+    border-radius: 16rpx;
+  }
+}
+.info-img {
+  width: 32rpx;
+  height: 32rpx;
+  margin-left: 16rpx;
+  background: #0bbc581a;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.edit-input {
+  flex: 1;
+  height: 48rpx;
+  border: 2rpx solid #0bbc58;
+  border-radius: 8rpx;
+  padding: 0 16rpx;
+  font-size: 28rpx;
+  color: #303133;
+  background-color: #f5f7fa;
+}
+.save-btn {
+  margin-left: 16rpx;
+  padding: 8rpx 20rpx;
+  background-color: #0bbc58;
+  color: #fff;
+  border: none;
+  border-radius: 8rpx;
+  font-size: 24rpx;
+  height: 48rpx;
+  line-height: 28rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+</style>

+ 20 - 20
pages/equipList2/index.vue

@@ -20,19 +20,19 @@
 					</view>
 				</view>
 				<view class="listbox" v-if="menuList.length">
-				<view class="second-tab">
-					<view class="tab-box">
-						<view
-              v-for="(item,index) in list"
-              :key="index"
-              @click="changeDevice(item,index)"
-							v-show="!item.url"
-							:class="['tab-item',currents == index?'active':'']"
-            >
-							<text>{{item.purview_name}}</text>
+					<view class="second-tab">
+						<view class="tab-box">
+							<view
+								v-for="(item,index) in list"
+								:key="index"
+								@click="changeDevice(item,index)"
+								v-show="!item.url"
+								:class="['tab-item',currents == index?'active':'']"
+							>
+								<text>{{item.purview_name}}</text>
+							</view>
 						</view>
 					</view>
-				</view>
 				<view class="third-tab">
 					<view 
             class="third-tab-item"
@@ -192,6 +192,14 @@
         const menulist = res || [];
         const menuItem = menulist.find(item => item.purview_name == '智慧物联')
         this.menuList = menuItem.children || [];
+				if(this.menuList.length == 0){
+					//提示暂无分配权限
+					uni.showToast({
+						title: '暂无分配权限,请联系工作人员',
+						icon: 'none',
+					});
+					return
+				}
 				if(!this.pur_id){
 					this.list = this.menuList[0].children || []
 					this.pur_id = this.menuList[0].pur_id || '';
@@ -351,16 +359,8 @@
 						}
 						break;
 					case 38:
-						var obj = {};
-						obj.d_id = item.d_id;
-						obj.equip_id = item.id;
-						obj.is_online = item.status;
-						obj.lat = item.lat;
-						obj.lng = item.lng;
-						obj.equip_name = item.name;
-						obj.uptime = item.uptime;
 						uni.navigateTo({
-							url: '../environment/equipment-new?shebei=' + JSON.stringify(obj),
+							url: `../deviceDetails/weatherStation1/index?devBid=${item.id}&devName=${item.name}&devStatus=${item.status}&address=${item.address}&uptime=${item.uptime}&d_id=${item.d_id}&deviceType=46`,
 						});
 						break;
 					case 5:

+ 13 - 1
pages/scd/deviceControl.vue

@@ -12,7 +12,7 @@
     <view class="device-detail__body">
       <view class="tabs">
         <view class="tab-container">
-          <view class="tab-item" :class="activeTab === 'pestAnalysis'?'active':''" @click="handleTabClick('pestAnalysis')">
+          <view class="tab-item" v-if="myuser_type" :class="activeTab === 'pestAnalysis'?'active':''" @click="handleTabClick('pestAnalysis')">
             管理员
           </view>
           <view class="tab-item" :class="activeTab === 'viewImage'?'active':''" @click="handleTabClick('viewImage')">
@@ -144,6 +144,7 @@ export default {
       show: false,
       checked: false,
       currentIndex: -1,
+      myuser_type: false,
       currentType: ''
     }
   },
@@ -157,6 +158,17 @@ export default {
         label:i < 10 ? '0' + i + ':00' : i + ':00'
       })
     }
+    uni.getStorage({
+      key:"myuser_type",
+      success:(res)=>{
+        if(Number(res.data) == 1){
+          this.myuser_type = true
+          this.activeTab = 'pestAnalysis'
+        }else{
+          this.activeTab = 'viewImage'
+        }
+      }
+    })
   },
   methods: {
     clearTime(){

+ 85 - 100
pages/sy/components/deviceData.vue

@@ -4,7 +4,7 @@
     <view class="device-data-wraper">
       <view class="top-bar">
         <view class="timestamp">
-          {{formatDate(deviceInfo.uptime)}}
+          {{formatDate(deviceInfo.addtime*1000)}}
           <u-icon name="reload" color="#0BBC58" style="margin-left: 12rpx" @click="refreshData"></u-icon>
         </view>
         <view class="icon-group">
@@ -21,40 +21,75 @@
       </view>
 
       <!-- 主要数据面板 -->
-      <view class="main-data-panel">
+       <view class="main-data-panel" v-if="deviceInfo.type_id == 33">
         <view class="data-column-left">
           <view class="data-item">
-            <text class="data-value">{{ deviceStatic.vol }}</text>
-            <text class="data-label">电</text>
+            <text class="data-value">{{ deviceStatic.voltage }}</text>
+            <text class="data-label">电</text>
           </view>
           <view class="data-item">
-            <text class="data-value">{{ deviceStatic.gs == 1? '落虫' : '排水' }}</text>
-            <text class="data-label">通道状态</text>
+            <text class="data-value">{{ deviceStatic.status }}</text>
+            <text class="data-label">设备状态</text>
           </view>
           <view class="data-item">
-            <text class="data-value">{{ deviceStatic.upds == 1? '打开' : '关闭' }}</text>
-            <text class="data-label">上仓门</text>
+            <text class="data-value">{{ deviceStatic.temperature }}</text>
+            <text class="data-label">环境温度</text>
           </view>
           <view class="data-item">
-            <text class="data-value">{{ deviceStatic.tph || '' }}℃</text>
-            <text class="data-label">高温限值</text>
+            <text class="data-value">{{ deviceStatic.lightDuration }}</text>
+            <text class="data-label">关照</text>
           </view>
         </view>
         <view class="data-column-left">
           <view class="data-item">
-            <text class="data-value">{{ deviceStatic.tmod == '1' ? '时控' : '光控' }}</text>
-            <text class="data-label">定时模式</text>
+            <text class="data-value">{{ deviceStatic.ctrlMode }}</text>
+            <text class="data-label">控制模式</text>
           </view>
           <view class="data-item">
-            <text class="data-value">{{ deviceStatic.hs == '1' ? '加热' : '正常' }}</text>
-            <text class="data-label">加热状态</text>
+            <text class="data-value">{{ deviceStatic.takePhotoIntervalMinutes }}</text>
+            <text class="data-label">拍照上传频率</text>
           </view>
           <view class="data-item">
-            <text class="data-value">{{ deviceStatic.dpds == '1' ? '打开' : '关闭' }}</text>
-            <text class="data-label">下仓门</text>
+            <text class="data-value">{{ deviceStatic.humidity }}</text>
+            <text class="data-label">环境湿度</text>
           </view>
           <view class="data-item">
-            <text class="data-value">{{ deviceStatic.dver }}</text>
+            <text class="data-value">{{ deviceStatic.version }}</text>
+            <text class="data-label">设备版本</text>
+          </view>
+        </view>
+        <view class="data-column-right">
+          <view class="device-image-container">
+            <image class="device-image" :class="{'device-image2': deviceInfo.type_id == 33}" :src="formatDevImg" mode="aspectFit"></image>
+          </view>
+        </view>
+      </view>
+      <view class="main-data-panel" v-else>
+        <view class="data-column-left">
+          <view class="data-item">
+            <text class="data-value">{{ deviceStatic.vbat }}</text>
+            <text class="data-label">电量</text>
+          </view>
+          <view class="data-item">
+            <text class="data-value">{{ deviceStatic.status }}</text>
+            <text class="data-label">设备状态</text>
+          </view>
+          <view class="data-item">
+            <text class="data-value">{{ deviceStatic.signal }}</text>
+            <text class="data-label">信号强度</text>
+          </view>
+        </view>
+        <view class="data-column-left">
+          <view class="data-item">
+            <text class="data-value">{{ deviceStatic.control }}</text>
+            <text class="data-label">控制模式</text>
+          </view>
+          <view class="data-item">
+            <text class="data-value">{{ deviceStatic.photo_fre }}/天</text>
+            <text class="data-label">拍照上传频率</text>
+          </view>
+          <view class="data-item">
+            <text class="data-value">{{ deviceStatic.dver_num }}</text>
             <text class="data-label">设备版本</text>
           </view>
         </view>
@@ -99,7 +134,7 @@
       <view class="history-table">
         <view class="table-container">
           <!-- 固定列 -->
-          <view class="fixed-column">
+          <!-- <view class="fixed-column">
             <view class="fixed-header">
               <text class="header-cell fixed">上报时间</text>
             </view>
@@ -110,25 +145,14 @@
                 class="fixed-row"
                 :class="{ even: index % 2 === 0 }"
               >
-                <view class="body-cell fixed">{{ formatTime(item.addtime) }}</view>
+                <view class="body-cell fixed">{{ item.UPDATE_DATE }}</view>
               </view>
             </view>
-          </view>
+          </view> -->
           <!-- 可滑动列 -->
           <view class="scrollable-column">
             <view class="scrollable-header">
-              <text class="header-cell">环境温度(°C)</text>
-              <text class="header-cell">环境湿度(%)</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>
-              <text class="header-cell">电流(mA)</text>
-              <text class="header-cell">电压(V)</text>
-              <text class="header-cell">经度</text>
-              <text class="header-cell">纬度</text>
+              <text class="header-cell" v-for="(item, index) in title" :key="index">{{ item.label }}</text>
             </view>
             <view class="scrollable-body">
               <view
@@ -136,30 +160,7 @@
                 :key="index"
                 class="scrollable-row"
               >
-                <view class="body-cell">{{ item.at }}</view>
-                <view class="body-cell">{{ item.ah }}</view>
-                <view class="body-cell">{{ item.hrt }}</view>
-                <view class="body-cell">
-                  <view class="status-icon" :class="item.rps == 0 ? 'success-icon' : 'status-icon'"></view>
-                  {{ getRpsDict(item) }}
-                </view>
-                <view class="body-cell">
-                  <view class="status-icon" :class="item.tps == 0 ? 'success-icon' : 'status-icon'"></view>
-                  {{ getTpsDict(item) }}
-                </view>
-                <view class="body-cell">
-                  <view class="status-icon" :class="item.lps == 0 ? 'success-icon' : 'status-icon'"></view>
-                  {{ getLpsDict(item) }}
-                </view>
-                <view class="body-cell">
-                  <view class="status-icon" :class="item.ws == 0 ? 'warning-icon' : item.lamp == 0 ? 'success-icon':item.lamp == 1 ? 'error-icon':'status-icon'"></view>
-                  {{ getStateDict(item) }}
-                </view>
-                <view class="body-cell">{{ item.csq }}</view>
-                <view class="body-cell">{{ item.current }}</view>
-                <view class="body-cell">{{ item.vbat }}</view>
-                <view class="body-cell">{{ item.lng }}</view>
-                <view class="body-cell">{{ item.lat }}</view>
+                <view class="body-cell" v-for="t in title" :key="t.value">{{ item[t.value] }}</view>
               </view>
             </view>
           </view>
@@ -286,6 +287,10 @@ export default {
       type:Number,
       default:10
     },
+    title:{
+      type:Array,
+      default:()=>[],
+    },
   },
   components: {
     floatButton,
@@ -312,7 +317,6 @@ export default {
       chartTabs: [
         { name: '温度', id: 'new_tem' },
         { name: '湿度', id: 'new_hum' },
-        { name: '加热仓温度', id: 'others' },
       ],
       opts: {
         type: 'line',
@@ -375,7 +379,7 @@ export default {
     polylineList:{
       handler(newVal, oldVal){
         this.xData = newVal.map(item => this.formatDate(new Date(item.addtime)));
-        this.yData = newVal.map(item => Number(item.new_tem) || 0);
+        this.yData = newVal.map(item => Number(item.tem) || 0);
         if(this.xData.length){
           this.initChart();
         }
@@ -385,27 +389,11 @@ export default {
     },
     deviceInfo:{
       handler(newVal, oldVal){
-        console.log(newVal,'newvalnewvalnewval')
-        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'
+        if (newVal.type_id == '34'){
+          this.devImg = 'sycb1'
           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 if(newVal.device_model == '16'){
-          this.devImg = 'lowcbd'
-          this.isShowPhoto = true
-        } else{
-          this.devImg = 'cbd'
+        } else {
+          this.devImg = 'sycb2'
           this.isShowPhoto = true
         }
       },
@@ -417,7 +405,7 @@ export default {
         this.historyData = []
         if(newVal.length > 0){
           newVal.forEach(item=>{
-            this.historyData.push(item.d_h_t)
+            this.historyData.push(item)
           })
         }
       },
@@ -552,22 +540,12 @@ export default {
     },
     // 格式化时间
     formatDate(dateString) {
-      const date = new Date(dateString*1000);
+      const date = new Date(dateString);
       const year = date.getFullYear();
       const month = String(date.getMonth() + 1).padStart(2, '0');
       const day = String(date.getDate()).padStart(2, '0');
       return `${year}-${month}-${day}`;
     },
-    formatTime(dateString) {
-      const date = new Date(dateString*1000);
-      const year = date.getFullYear();
-      const month = String(date.getMonth() + 1).padStart(2, '0');
-      const day = String(date.getDate()).padStart(2, '0');
-      const hour = String(date.getHours()).padStart(2, '0');
-      const minute = String(date.getMinutes()).padStart(2, '0');
-      const second = String(date.getSeconds()).padStart(2, '0');
-      return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
-    },
     drawChart() {
       // 销毁已有的图表实例
       if (chartInstance) {
@@ -681,12 +659,10 @@ export default {
       this.activeChartTab = index;
       // 根据不同标签切换数据
       if (this.chartTabs[index].id === 'new_tem') {
-        this.yData = this.polylineList.map(item => Number(item.new_tem) || 0);
+        this.yData = this.polylineList.map(item => Number(item.hum) || 0);
       } else if (this.chartTabs[index].id === 'new_hum') {
-        this.yData = this.polylineList.map(item => Number(item.new_hum) || 0);
-      } else if (this.chartTabs[index].id === 'others') {
-        this.yData = this.polylineList.map(item => Number(item.others) || 0);
-      }
+        this.yData = this.polylineList.map(item => Number(item.tem) || 0);
+      } 
       this.$nextTick(() => {
         // this.drawChart();
 		    this.updateChartsData();
@@ -934,10 +910,13 @@ export default {
 
       .device-image {
         position: relative;
-        width: 260rpx;
-        height: 360rpx;
+        width: 220rpx;
+        height: 340rpx;
         z-index: 1;
-        top: 60rpx;
+        top: 20rpx;
+      }
+      .device-image2 {
+        top: 70rpx;
       }
     }
 
@@ -1190,7 +1169,7 @@ export default {
           padding: 20rpx 0;
 
           .header-cell {
-            min-width: 140rpx;
+            min-width: 160rpx;
             font-size: 24rpx;
             font-family: 'Source Han Sans CN VF', sans-serif;
             font-weight: 500;
@@ -1201,6 +1180,9 @@ export default {
             text-overflow: ellipsis;
             white-space: nowrap;
             margin-right: 10rpx;
+            &:first-child {
+              min-width: 220rpx;
+            }
           }
         }
 
@@ -1213,7 +1195,7 @@ export default {
             align-items: center;
 
             .body-cell {
-              min-width: 140rpx;
+              min-width: 160rpx;
               font-size: 24rpx;
               font-family: 'Source Han Sans CN VF', sans-serif;
               font-weight: 400;
@@ -1223,6 +1205,9 @@ export default {
               display: flex;
               align-items: center;
               justify-content: center;
+              &:first-child {
+                min-width: 220rpx;
+              }
             }
           }
         }

+ 37 - 19
pages/sy/detail.vue

@@ -111,6 +111,7 @@
           :deviceHistoryList="deviceHistoryList"
           :totalPage="totalPage"
           :currentPage="page"
+          :title="title"
           :page_size="page_size"
           @prevPage="prevPage"
           @nextPage="nextPage"
@@ -176,6 +177,7 @@ export default {
       is_pest:'',
       page_size:24,
       pestList:[],
+      title:[],
       pest_names:'',
       pests:[],
       polylineList:[],
@@ -259,9 +261,15 @@ export default {
       })
     },
     handleSettingClick(){
-      uni.navigateTo({
-        url: '/pages/sy/deviceControl?deviceId=' + this.deviceInfo.id + '&d_id=' + this.deviceInfo.d_id + '&device_type=' + this.deviceInfo.type,
-      });
+      if(this.deviceInfo.type_id == 34){
+        uni.navigateTo({
+          url: '/pages/sy/deviceControl?deviceId=' + this.deviceInfo.id + '&d_id=' + this.deviceInfo.d_id + '&device_type=' + this.deviceInfo.type,
+        });
+      }else{
+        uni.navigateTo({
+          url: '/pages/sy/deviceControl2?deviceId=' + this.deviceInfo.id + '&d_id=' + this.deviceInfo.d_id + '&device_type=' + this.deviceInfo.type,
+        });
+      }
     },
     closeOperationHandler(){
       this.isShowOperation = false;
@@ -400,10 +408,11 @@ export default {
     },
     async getDeviceData(){
       const res = await this.$myRequest({
-        url: '/api/api_gateway?method=forecast.worm_lamp.device_status_data',
+        url: '/api/api_gateway?method=new_gateway.device_info.device_status',
         method: 'POST',
         data: {
-          device_id: this.deviceInfo.id,
+          id: this.deviceInfo.d_id,
+          device_type_id: this.deviceInfo.type,
         },
       });
       this.deviceStatic = res;
@@ -456,33 +465,42 @@ export default {
     },
     async getPolylineData(){
       const res = await this.$myRequest({
-        url: '/api/api_gateway?method=forecast.worm_lamp.device_polyline_data',
+        url: '/api/api_gateway?method=new_gateway.data_info.temp_hum',
         method: 'POST',
         data: {
           device_type_id: this.deviceInfo.type_id,
-          d_id: this.deviceInfo.d_id,
-          start_time: new Date(this.startDate + ' 00:00:00').getTime()/1000,// 转成毫秒
-          end_time: new Date(this.endDate + ' 23:59:59').getTime()/1000,
+          id: this.deviceInfo.d_id,
+          start: new Date(this.startDate + ' 00:00:00').getTime()/1000,// 转成毫秒
+          end: new Date(this.endDate + ' 23:59:59').getTime()/1000,
         },
       });
-      const data = res || [];
+      const data = res?.data || [];
       this.polylineList = data
     },
     async getDeviceHistoryData(){
       const res = await this.$myRequest({
-        url: '/api/api_gateway?method=forecast.worm_lamp.device_history_data',
+        url: '/api/api_gateway?method=new_gateway.data_info.history_data',
         method: 'POST',
         data: {
           device_type_id: this.deviceInfo.type_id,
-          device_id: this.deviceInfo.id,
-          start_time: new Date(this.startDate + ' 00:00:00').getTime()/1000,
-          end_time: new Date(this.endDate + ' 23:59:59').getTime()/1000,
+          id: this.deviceInfo.d_id,
+          start: new Date(this.startDate + ' 00:00:00').getTime()/1000,
+          end: new Date(this.endDate + ' 23:59:59').getTime()/1000,
           page: this.page,
           page_size: this.page_size,
         },
       });
       const data = res?.data || [];
-      this.totalPage = res.counts;
+      this.totalPage = res.total || 0;
+      const title = res?.title || {};
+      const newTitle = [];
+      for(let t in title){
+        newTitle.push({
+          label:title[t],
+          value:t
+        });
+      }
+      this.title = newTitle;
       this.deviceHistoryList = data
     },
     async getPestAnalysis(){
@@ -490,10 +508,10 @@ export default {
         url: '/api/api_gateway?method=forecast.cbd_analysis.analysis_pest_result',
         method: 'POST',
         data: {
-        d_id: this.deviceInfo.d_id,
-        start: this.startDate,
-        end: this.endDate,
-        model: 'B'
+          d_id: this.deviceInfo.d_id,
+          start: this.startDate,
+          end: this.endDate,
+          model: 'B'
         },
       });
       const pest_order = res?.pest_order;

+ 13 - 1
pages/sy/deviceControl.vue

@@ -13,7 +13,7 @@
     <view class="device-detail__body">
       <view class="tabs">
         <view class="tab-container">
-          <view class="tab-item" :class="activeTab === 'pestAnalysis'?'active':''" @click="handleTabClick('pestAnalysis')">
+          <view class="tab-item" v-if="myuser_type" :class="activeTab === 'pestAnalysis'?'active':''" @click="handleTabClick('pestAnalysis')">
             管理员
           </view>
           <view class="tab-item" :class="activeTab === 'viewImage'?'active':''" @click="handleTabClick('viewImage')">
@@ -163,6 +163,7 @@ export default {
       sliderField: '',
       sliderMin: 0,
       sliderMax: 100,
+      myuser_type: false,
       sliderRect: null
     }
   },
@@ -184,6 +185,17 @@ export default {
         label: j + ':00'
       })
     }
+    uni.getStorage({
+      key:"myuser_type",
+      success:(res)=>{
+        if(Number(res.data) == 1){
+          this.myuser_type = true
+          this.activeTab = 'pestAnalysis'
+        }else{
+          this.activeTab = 'viewImage'
+        }
+      }
+    })
     this.list2 = [time1, time2];
     this.deviceType = options.device_type
     this.getControlDeviceConfigInfo()

+ 575 - 0
pages/sy/deviceControl2.vue

@@ -0,0 +1,575 @@
+<template>
+  <view class="device-detail">
+    <view class="device-detail__header">
+      <u-icon
+        size="36"
+        class="arrow-left"
+        name="arrow-left"
+        @click="handleBack"
+      ></u-icon>
+      {{ title }}
+    </view>
+		<u-select v-model="show" :list="list" @confirm="confirmHandler"></u-select>
+    <view class="device-detail__body">
+      <view class="tabs">
+        <view class="tab-container">
+          <view class="tab-item" v-if="myuser_type" :class="activeTab === 'pestAnalysis'?'active':''" @click="handleTabClick('pestAnalysis')">
+            管理员
+          </view>
+          <view class="tab-item" :class="activeTab === 'viewImage'?'active':''" @click="handleTabClick('viewImage')">
+            设置
+          </view>
+        </view>
+      </view>
+      <view class="device-detail-content" v-if="activeTab === 'pestAnalysis'">
+        <view class="device-detail-item">
+          <text class="device-detail-label">识别种类</text>
+          <view class="device-detail-btn-container">
+            <u-button @click="show2 = true" size="mini">{{getSelectPest(selectPest)}}</u-button>
+            <view class="device-detail-btn" @click="setBindPest">{{selectPest.pest_id == '' ? '恢复默认' : '绑定设备'}}</view>
+          </view>
+        </view>
+        <view class="device-detail-item">
+          <text class="device-detail-label">联网模块</text>
+          <view class="device-detail-btn-container">
+            <view class="device-detail-btn" @click="setDeviceContorl('reboot')">数据单元重启</view>
+            <view class="device-detail-btn" @click="setDeviceContorl('upgradeApp')">数据单元升级</view>
+          </view>
+        </view>
+        <view class="device-detail-item">
+          <text class="device-detail-label">设备操作</text>
+          <view class="device-detail-btn-container">
+            <view class="device-detail-btn" @click="setDeviceContorl('takephoto')">测试拍照</view>
+            <view class="device-detail-btn" @click="setDeviceContorl('getLocation')">获取定位</view>
+          </view>
+        </view>
+      </view>
+      <view class="device-detail-content" style="padding-bottom: 50rpx" v-if="activeTab === 'viewImage'">
+        <view class="device-detail-viewImage">
+          <text class="device-detail-label">拍照频率(min):</text>
+          <text>
+            <u-input
+              v-model="equipContrlForm.takePhotoIntervalMinutes"
+              type="number"
+              :border="false"
+              height="40"
+              auto-height="true"
+              input-align="right"
+            />
+          </text>
+        </view>
+        <view class="device-detail-viewImage">
+          <text class="device-detail-label">控制模式:</text>
+          <text @click="show = true">{{ formatCtrlMode(equipContrlForm.ctrlMode) }}</text>
+        </view>
+        <view class="device-detail-viewImage">
+          <text class="device-detail-label">时控时长:</text>
+          <text @click="show1 = true">{{ fromTime(equipContrlForm.startHour) }} - {{ fromTime(equipContrlForm.endHour) }}</text>
+        </view>
+      </view>
+    </view>
+    <view class="device-detail-btn-footer" v-if="activeTab === 'viewImage'">
+      <view class="device-detail-btn" @click="saveSettings">保存</view>
+    </view>
+	  <u-select v-model="show1" mode="mutil-column" :list="list2" @confirm="confirmDateHandler"></u-select>
+	  <u-select v-model="show2" :list="list3" @confirm="confirmDateHandler2"></u-select>
+  </view>
+</template>
+<script>
+
+export default {
+  data(){
+    return {
+      show: false,
+      show1: false,
+      show2: false,
+      list2:[],
+      list3:[],
+      list: [
+        { value: '0', label: '光控模式' },
+        { value: '1', label: '时控模式' },
+      ],
+      selectPest: {},
+      value: 10,
+      cmd1: '0001',
+      imageList: [],
+      equipContrlForm: {},
+      time: '',
+      activeTab: 'pestAnalysis',
+      title: '设置控制',
+      deviceType: '',
+      location: '',
+      d_id: '',
+      checked: false,
+      sliderField: '',
+      sliderMin: 0,
+      sliderMax: 100,
+      myuser_type: false,
+      sliderRect: null
+    }
+  },
+  onLoad(options){
+    this.d_id = options.d_id
+    const time1 = [];
+    const time2 = [];
+    for(let i = 0;i< 24;i++){
+      let j = i;
+      if(i < 10){
+        j = '0' + i
+      }
+      time1.push({
+        value: i,
+        label: j + ':00'
+      })
+      time2.push({
+        value: i,
+        label: j + ':00'
+      })
+    }
+    uni.getStorage({
+      key:"myuser_type",
+      success:(res)=>{
+        if(Number(res.data) == 1){
+          this.myuser_type = true
+          this.activeTab = 'pestAnalysis'
+        }else{
+          this.activeTab = 'viewImage'
+        }
+      }
+    })
+    this.list2 = [time1, time2];
+    this.deviceType = options.device_type
+    this.getControlDeviceConfigInfo()
+    this.getFollowPest()
+    this.getAllPestList()
+  },
+  methods: {
+    getSelectPest(selectPest){
+      if(selectPest?.pest_name == '清空' || !selectPest?.pest_name){
+        return '请选择识别种类'
+      }else{
+        return selectPest.pest_name
+      }
+    },
+    async getAllPestList() {
+      const res = await this.$myRequest({
+        url: '/api/api_gateway?method=forecast.pest_info.pest_dict',
+        data: {
+          type_name: 1
+        }
+      })
+      const pestList = [];
+      for(let pest in res){
+        pestList.push({
+          value: pest,
+          label: res[pest]
+        })
+      }
+      pestList.unshift({
+        value: '',
+        label: '清空'
+      })
+      this.list3 = pestList
+    },
+    formatCtrlMode(ctrlMode){
+      if(ctrlMode == 1){
+        return '时控模式'
+      }else{
+        return '光控模式'
+      }
+    },
+    getProgressWidth(value, min, max) {
+      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
+      const query = uni.createSelectorQuery().in(this)
+      query.select('.custom-progress').boundingClientRect(rect => {
+        this.sliderRect = rect
+      }).exec()
+      this.updateSliderValue(e.touches[0].clientX, field, min, max)
+    },
+    onSliderTouchMove(e, min, max) {
+      if (!this.sliderField || !this.sliderRect) return
+      this.updateSliderValue(e.touches[0].clientX, this.sliderField, min, max)
+    },
+    onSliderTouchEnd() {
+      this.sliderField = ''
+    },
+    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()
+    },
+    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)
+    },
+    fromTime(time){
+      if(time < 10){
+        time = '0' + time + ':00'
+      }else{
+        time = time + ':00'
+      }
+      return time
+    },
+    confirmDateHandler2(e){
+      console.log(e,'e')
+      this.show2 = false
+      this.selectPest.pest_id = e[0].value
+      this.selectPest.pest_name = e[0].label
+    },
+    confirmDateHandler(e){
+      console.log(e,'e')
+      this.show1 = false
+      this.equipContrlForm.startHour = e[0].value
+      this.equipContrlForm.endHour = e[1].value
+    },
+    confirmHandler(item){
+      this.equipContrlForm.ctrlMode = item[0].value
+    },
+    formart(cmd1){
+      let text = ''
+      this.list.forEach(item => {
+        if(item.value === cmd1){
+          text = item.label
+        }
+      })
+      return text
+    },
+    handleBack() {
+      uni.navigateBack({
+        delta: 1
+      });
+    },
+    handleTabClick(tab) {
+      this.activeTab = tab;
+    },
+    async getFollowPest(){
+      const res = await this.$myRequest({
+        url: '/api/api_gateway?method=forecast.new_cbd.get_follow_pest',
+        data: {
+          d_id: this.d_id,
+        }
+      })
+      if (res) {
+        this.selectPest = res?.pest_list[0] || {}
+      } else {
+        this.showToast({
+          title: '获取绑定设备失败',
+          icon: 'error',
+        });
+      }
+    },
+    setBindPest(){
+      let pest_data = {}
+      if(this.selectPest.pest_id){
+        pest_data = JSON.stringify([{
+          pest_id: this.selectPest.pest_id,
+          pest_name: this.selectPest.pest_name,
+        }]);
+      }else{
+        pest_data = ''
+      }
+      const data = {
+        d_id: this.d_id,
+        disable: 0,
+        pest_data
+      }
+      this.$myRequest({
+        url: '/api/api_gateway?method=forecast.new_cbd.modify_follow_pest',
+        method: 'POST',
+        data
+      }).then(res => {
+        if (res) {
+          uni.showToast({
+            title: '绑定设备成功!',
+            icon: 'success',
+          });
+        } else {
+          uni.showToast({
+            title: '绑定设备失败',
+            icon: 'error',
+          });
+        }
+      })
+    },
+    async setDeviceContorl(type){
+      const data = {
+        device_type_id: this.deviceType,
+        d_id: this.d_id,
+        cmd: type,
+      }
+      if(type === 'imei'){
+        data.imei = this.equipContrlForm.imei
+      }else{
+        delete data.imei
+      }
+      this.$myRequest({
+        url: '/api/api_gateway?method=forecast.send_control.admin_device_control',
+        method: 'POST',
+        data
+      }).then(res => {
+        if (res) {
+          uni.showToast({
+            title: '设备控制修改成功!',
+            icon: 'success',
+          });
+        } else {
+          uni.showToast({
+            title: '设备控制修改失败',
+            icon: 'error',
+          });
+        }
+      });
+    },
+    saveSettings(){
+      let newForm = Object.assign({}, this.equipContrlForm) // 深拷贝
+      this.$myRequest({
+        url: '/api/api_gateway?method=new_gateway.device_info.update_device_config',
+        method: 'POST',
+        data: {
+          device_type_id: this.deviceType,
+          id: this.d_id,
+          ...newForm,
+        }
+      }).then(res => {
+        if (res) {
+          // 设备控制修改成功
+          uni.showToast({
+            title: '设备控制修改成功!',
+            icon: 'success',
+          });
+        } else {
+          uni.showToast({
+            title: '设备控制修改失败',
+            icon: 'error',
+          });
+        }
+      })
+    },
+    async getControlDeviceConfigInfo(){
+      const res = await this.$myRequest({
+        url: '/api/api_gateway?method=new_gateway.device_info.get_device_config',
+        method: 'POST',
+        data: {
+          device_type_id: this.deviceType,
+          id: this.d_id,
+        },
+      });
+      this.equipContrlForm = res
+    },
+  }
+}
+</script>
+<style scoped lang="scss">
+.device-detail {
+  display: flex;
+  width: 100%;
+  height: calc(100vh - 112rpx);
+  padding-top: 112rpx;
+  flex-direction: column;
+  align-items: center;
+  background: linear-gradient(180deg, #ffffff00 0%, #F5F6FA 23.64%, #F5F6FA 100%), linear-gradient(102deg, #BFEADD 6.77%, #B8F1E7 40.15%, #B9EEF5 84.02%);
+  .device-detail__header {
+    width: 100%;
+    font-size: 28rpx;
+    color: #999;
+    color: #042118;
+    font-family: 'Source Han Sans CN VF';
+    font-weight: 700;
+    position: relative;
+    text-align: center;
+    .arrow-left {
+      position: absolute;
+      left: 32rpx;
+      margin-right: 12rpx;
+    }
+  }
+  .device-detail__body {
+    width: calc(100% - 64rpx);
+    margin: 0 auto;
+    border-radius: 16rpx;
+    overflow-x: hidden;
+    overflow-y: auto;
+    // 隐藏滚动条
+    -ms-overflow-style: none;
+    scrollbar-width: none;
+  }
+  .tabs {
+    margin: 24rpx 0;
+    border-radius: 16rpx;
+    padding: 16rpx 0;
+    padding-top: 0;
+    .tab-container{
+      display: flex;
+      width: 100%;
+      height: 88rpx;
+      line-height: 88rpx;
+      text-align: center;
+      font-size: 28rpx;
+      font-weight: 700;
+      color: #042118;
+      font-family: 'Source Han Sans CN VF';
+    }
+    .tab-item {
+      margin-right: 40rpx;
+      color:#999999;
+    }
+    .active{
+      position: relative;
+      color: #303133;
+      text-align: center;
+      font-family: "Source Han Sans CN VF";
+      font-size: 28rpx;
+      font-weight: 700;
+      &::after {
+        content: '';
+        position: absolute;
+        bottom: 10rpx;
+        left: 50%;
+        transform: translateX(-50%);
+        width: 100%;
+        height: 36rpx;
+        border-bottom: 6rpx solid #303133;
+      }
+    }
+  }
+  .device-detail-content{
+    display: flex;
+    padding: 24rpx 32rpx;
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 20rpx;
+    border-radius: 16rpx;
+    background: #FFF;
+    .device-detail-item{
+      .device-detail-label{
+        color: #303133;
+        font-family: "Source Han Sans CN VF";
+        font-size: 28rpx;
+        font-weight: 400;
+      }
+      .device-detail-btn-container{
+        display: flex;
+        gap: 24rpx;
+        margin-top: 12rpx;
+      }
+      .device-detail-btn{
+        display: flex;
+        padding: 10rpx 16rpx;
+        justify-content: center;
+        align-items: center;
+        gap: 16rpx;
+        border-radius: 16rpx;
+        background: #0BBC58;
+        color: #ffffff;
+        font-family: "Source Han Sans CN VF";
+        font-size: 24rpx;
+        font-weight: 400;
+      }
+      .force-btn{
+        background: #FB4E52;
+      }
+    }
+    .device-detail-viewImage{
+      width: 100%;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding-bottom: 20rpx;
+    }
+  }
+  .slider-container{
+    position: relative;
+    margin: 10rpx 0 20rpx 0;
+    .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;
+      transition: width 0.2s;
+    }
+    .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);
+      transition: left 0.2s;
+    }
+  }
+  .device-detail-btn-footer{
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    width: 100%;
+    height: 112rpx;
+    line-height: 112rpx;
+    text-align: center;
+    font-size: 28rpx;
+    font-weight: 700;
+    color: #042118;
+    font-family: 'Source Han Sans CN VF';
+    background: #ffffff;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    .device-detail-btn{
+      display: flex;
+      height: 80rpx;
+      width: 90%;
+      margin: 0 auto;
+      padding: 0rpx 20rpx;
+      justify-content: center;
+      align-items: center;
+      gap: 8rpx;
+      border-radius: 16rpx;
+      background:#0BBC58;
+      color: #ffffff;
+      text-align: center;
+      font-family: "Source Han Sans CN VF";
+      font-size: 14px;
+      font-style: normal;
+      font-weight: 500;
+    }
+  }
+}
+</style>

BIN
static/images/device/qxz_low.png


+ 2 - 2
util/api.js

@@ -1,9 +1,9 @@
 // let BASE_URL = 'http://114.55.0.7:8002';
 // const BASE_URL='http://8.136.98.49:8002'
 // let BASE_URL = 'http://218.28.198.186:10505'
-// let BASE_URL = 'https://wx.hnyfwlw.com'
+let BASE_URL = 'https://wx.hnyfwlw.com'
 // let BASE_URL = 'http://192.168.1.107:8000';
-let BASE_URL = 'http://218.28.198.186:10508';
+// let BASE_URL = 'http://218.28.198.186:10508';
 // let BASE_URL = 'https://uat.hnyfwlw.com'
 export const myRequest = (options) => {
   // BASE_URL=uni.getStorageSync('http')