Quellcode durchsuchen

设备管理功能模块

fanghuisheng vor 1 Jahr
Ursprung
Commit
8132bac297

+ 28 - 2
src/api/business/fireIot/deviceSelect/index.js

@@ -1,5 +1,4 @@
-import upload from "@/utils/upload";
-import request from "@/utils/request";
+import { request } from "@/utils/request";
 
 // 设备查询列表请求
 export function dmpProductInfo(param) {
@@ -18,3 +17,30 @@ export function dmpDeviceInfo(param) {
         data: param,
     });
 }
+
+// 设备详情属性列表请求
+export function dmpProductAttribute(param) {
+    return request({
+        url: "/service-iot/dmpProductAttribute/page",
+        method: "POST",
+        data: param,
+    });
+}
+
+// 设备多属性历史数据请求
+export function historyMetrics(param) {
+    return request({
+        url: "/usky-backend/dataQuery/historyMetrics",
+        method: "POST",
+        data: param,
+    });
+}
+
+// 设备实时数据请求
+export function last(param) {
+    return request({
+        url: "/usky-backend/dataQuery/last",
+        method: "POST",
+        data: param,
+    });
+}

+ 278 - 0
src/pages/business/fireIot/deviceSelect/components/chart.vue

@@ -0,0 +1,278 @@
+<template>
+  <view class="content">
+    <l-echart ref="domMyChart" class="echarts"></l-echart>
+  </view>
+</template>
+
+<script setup>
+import * as echarts from "echarts";
+import { onLoad, onShow, onHide, onLaunch, onResize } from "@dcloudio/uni-app";
+import { defineComponent, ref, onMounted, nextTick, watch } from "vue";
+
+const props = defineProps({
+  currentDateList: {
+    type: Object,
+    default: null,
+  },
+});
+
+const domMyChart = ref(null);
+let myChart;
+let option;
+
+var legendData = [];
+var seriesData = [
+  {
+    name: "告警次数",
+    type: "line",
+    data: [
+      ["2023-06-07", 9],
+      ["2023-06-08", 12],
+      ["2023-06-09", 13],
+      ["2023-06-10", 12],
+      ["2023-06-11", 25],
+      ["2023-06-12", 4],
+      ["2023-06-13", 59],
+      ["2023-06-14", 3],
+    ],
+    emphasis: {
+      focus: "series",
+      blurScope: "coordinateSystem",
+    },
+    symbolSize: 5,
+  },
+  {
+    name: "处理次数",
+    type: "line",
+    data: [
+      ["2023-06-07", 0],
+      ["2023-06-08", 0],
+      ["2023-06-09", 0],
+      ["2023-06-10", 0],
+      ["2023-06-11", 0],
+      ["2023-06-12", 0],
+      ["2023-06-13", 0],
+      ["2023-06-14", 0],
+    ],
+    emphasis: {
+      focus: "series",
+      blurScope: "coordinateSystem",
+    },
+    symbolSize: 5,
+  },
+  {
+    name: "告警次数1",
+    type: "line",
+    data: [
+      ["2023-06-07", 9],
+      ["2023-06-08", 12],
+      ["2023-06-09", 13],
+      ["2023-06-10", 12],
+      ["2023-06-11", 25],
+      ["2023-06-12", 4],
+      ["2023-06-13", 59],
+      ["2023-06-14", 3],
+    ],
+    emphasis: {
+      focus: "series",
+      blurScope: "coordinateSystem",
+    },
+    symbolSize: 5,
+  },
+  {
+    name: "处理次数1",
+    type: "line",
+    data: [
+      ["2023-06-07", 0],
+      ["2023-06-08", 0],
+      ["2023-06-09", 0],
+      ["2023-06-10", 0],
+      ["2023-06-11", 0],
+      ["2023-06-12", 0],
+      ["2023-06-13", 0],
+      ["2023-06-14", 0],
+    ],
+    emphasis: {
+      focus: "series",
+      blurScope: "coordinateSystem",
+    },
+    symbolSize: 5,
+  },
+];
+
+function initData() {
+  legendData = [];
+  seriesData = [];
+
+  if (props.currentDateList.length > 0) {
+    props.currentDateList.forEach((el) => {
+      legendData.push(el.attributeName);
+
+      seriesData.push({
+        name: el.attributeName,
+        type: "line",
+        data: el.data,
+        emphasis: {
+          focus: "series",
+          blurScope: "coordinateSystem",
+        },
+        symbolSize: 5,
+      });
+    });
+  } else {
+    legendData = ["暂无数据"];
+    seriesData = [
+      {
+        // smooth: true,  //设置折线为圆滑曲线,false则有转折点
+        name: "暂无数据",
+        type: "line",
+        data: [
+          ["2023-06-07", 0],
+          ["2023-06-14", 0],
+        ],
+        emphasis: {
+          focus: "series",
+          blurScope: "coordinateSystem",
+        },
+        symbolSize: 5,
+        // symbol: "circle",
+      },
+    ];
+  }
+
+  option = {
+    tooltip: {
+      backgroundColor: "rgba(255, 255, 255, 0.8)",
+      borderColor: "rgba(0, 0, 0, 0.1)",
+      order: "valueDesc",
+      trigger: "axis",
+      // formatter: function (params) {
+      //   var res = ``;
+      //   res += `
+      //           <view>${params[0].axisValueLabel.split(" ")[0]}</view>
+      //           `;
+      //   for (let i in params) {
+      //     res += `
+      //           <view style="display:flex">
+      //               <view style="width:10px;height:10px;background:${params[i].color};border-radius: 10px;margin:10px 0;"></view>
+      //               <view style="padding:4px 0px 0px 10px;">${params[i].seriesName} :</view>
+      //               <view style="padding:4px 0px 0px 10px;">${params[i].data[1]}</view>
+      //           </view>`; //可以在这个方法中做改变
+      //   }
+      //   return res;
+      // },
+    },
+    legend: {
+      type: "scroll", //设置超出滚动
+      bottom: 0,
+      right: "auto",
+      left: "center",
+      padding: [5, 10, 18, 10],
+      data: legendData,
+      itemGap: 30, // 图例间隔
+      itemWidth: 30, // 图例宽度
+      itemHeight: 10, //高度
+      textStyle: {
+        fontSize: "12",
+        fontFamily: "font-family: Microsoft YaHei Regular, Microsoft YaHei Regular-Regular;",
+      },
+      formatter: function (name) {
+        return name;
+      },
+    },
+    grid: {
+      left: "3%",
+      top: 30,
+      right: "4%",
+      bottom: "15%",
+      containLabel: true,
+    },
+    toolbox: {
+      show: false,
+    },
+    xAxis: {
+      type: "time",
+      boundaryGap: false,
+      nameTextStyle: {
+        // 名称样式
+        fontSize: 12,
+        color: "#fff",
+        fontWeight: "bold",
+      },
+      axisLine: {
+        lineStyle: {
+          // color: "rgba(0,0,0,0.1)",
+        },
+        // symbol: ["none", "arrow"], //轴线两边的箭头
+        symbolSize: [8, 12],
+      },
+      axisLabel: {
+        show: false,
+        showMaxLabel: true,
+        formatter: {
+          year: "{yyyy}",
+          month: "{MM}-{dd}",
+          // day: "{MM}-{dd} {hh}:{mm}",
+          day: "{dd}",
+          hour: "{MM}-{dd} {HH}:{mm}",
+          minute: "{HH}:{mm}",
+          second: "{HH}:{mm}:{ss}",
+          millisecond: "{hh}:{mm}:{ss} {SSS}",
+          none: "{yyyy}-{MM}-{dd} {hh}:{mm}:{ss} {SSS}",
+        },
+      },
+    },
+    yAxis: {
+      type: "value",
+    },
+    series: seriesData,
+  };
+}
+
+function initEcharts() {
+  // let dom = uni.createSelectorQuery().select("#linEcharts");
+  // myChart = echarts.init(document.getElementById("linEcharts"));
+  // 观测更新的数据在 view 层可以直接访问到
+  // myChart.setOption(option);
+
+  myChart = domMyChart.value;
+  myChart.init(echarts, (myChart) => {
+    myChart.setOption(option);
+  });
+}
+
+onLoad(() => {
+  nextTick(() => {
+    initData();
+    initEcharts();
+  });
+});
+
+onResize(() => {
+  myChart.resize();
+});
+
+watch(
+  () => props.currentDateList,
+  (val) => {
+    initData();
+    initEcharts();
+  }
+);
+</script>
+
+<style>
+.content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.echarts {
+  width: 100%;
+  height: 600rpx;
+  /* margin-top: 70rpx; */
+  /* background:pink */
+}
+</style>

+ 193 - 18
src/pages/business/fireIot/deviceSelect/components/deviceDetails.vue

@@ -5,18 +5,18 @@
         <image style="width: 40px; height: 40px; margin: auto 15px auto 0" :src="'/static/images/404.png'" mode="aspectFill"></image>
 
         <view style="margin: auto auto auto 0">
-          <view style="font-size: 15px"> 火警设备1 </view>
+          <view style="font-size: 15px"> {{ publicStore.$state.deviceDetailsArray.deviceName }} </view>
         </view>
 
         <view style="margin: auto 0 auto 0">
-          <!-- {{ siteList.siteStatus == null }} -->
-          <view style="font-size: 15px; color: #30bb00">在线</view>
-          <!-- #f07d28 -->
+          <view :style="{ fontSize: '15px', color: publicStore.$state.deviceDetailsArray.deviceStatus == 1 ? '#30bb00' : '#ff6000' }">
+            {{ publicStore.$state.deviceDetailsArray.deviceStatus == 1 ? "在线" : "离线" }}
+          </view>
         </view>
       </view>
 
       <view class="bg-white padding-15 margin-b-15">
-        <uni-section class="margin-bottom-10" title="基本信息" type="line"></uni-section>
+        <uni-section class="margin-b-10" title="基本信息" type="line"></uni-section>
 
         <view class="tableType3 padding-0">
           <u-empty v-if="dataList.length <= 0" text="暂无数据" mode="data" icon="http://cdn.uviewui.com/uview/empty/data.png"> </u-empty>
@@ -33,16 +33,61 @@
       </view>
 
       <view class="bg-white padding-15 margin-b-15">
-        <uni-section class="margin-bottom-10" title="地址信息" type="line"></uni-section>
+        <!-- 分段器组件 -->
+        <view class="app-subsection">
+          <u-subsection :list="list" :current="tabPosition" inactiveColor="#303133" :activeColor="proxy.$settingStore.themeColor.color" @change="tabPositionChange"></u-subsection>
+        </view>
 
-        <view> 徐乐路208号C幢 </view>
-      </view>
+        <view v-if="tabPosition == 1">
+          <view class="flex" :style="{ color: proxy.$settingStore.themeColor.color }">
+            <view class="margin-l-10" style="margin-left: auto" @click="open">选择时间</view>
+            <view class="margin-l-10" @click="modalShow = true">筛选</view>
+          </view>
 
-      <view class="bg-white padding-15 margin-b-15" style="height: 170px; max-height: 170px">
-        <uni-section class="margin-bottom-10" title="图表" type="line"></uni-section>
+          <chart :currentDateList="currentDateList"></chart>
 
-        <view style="height: calc(100% - 25px)"> </view>
+          <!-- <view class="tableType1">
+            <u-row>
+              <u-col span="12">
+                <view>指数</view>
+              </u-col>
+            </u-row>
+            <u-row v-for="(co, index) in content4" :key="index">
+              <u-col span="12">
+                <view>{{ co.name1 }}</view>
+              </u-col>
+            </u-row>
+          </view> -->
+        </view>
+        <!--  -->
+        <view v-else>
+          <view class="flex" style="flex-wrap: wrap; line-height: 36px">
+            <view style="width: 50%" v-for="realTime in realTimeDataList" :key="realTime">
+              {{ realTime.attributeName + ":" }}
+              {{ realTime.value }}
+              {{ realTime.attributeUnit ? realTime.attributeUnit : "" }}
+            </view>
+          </view>
+        </view>
       </view>
+
+      <u-modal :show="modalShow" @confirm="modalShow = false" @close="modalShow = false" :closeOnClickOverlay="true">
+        <view class="slot-content">
+          <u-checkbox-group
+            v-model="checkboxValueList"
+            @change="
+              (val) => {
+                checkboxChange(val);
+              }
+            "
+            :activeColor="proxy.$settingStore.themeColor.color"
+          >
+            <u-checkbox class="margin-b-10" v-for="option in checkboxDataList" :key="option" :label="option.attributeName" :name="option.attributeCode"> </u-checkbox>
+          </u-checkbox-group>
+        </view>
+      </u-modal>
+
+      <uni-calendar ref="calendar" class="uni-calendar--hook" :clearDate="false" :insert="false" :lunar="false" :range="true" @confirm="calendarConfirm" />
     </view>
   </scroll-view>
 </template>
@@ -52,31 +97,155 @@ import { onLoad, onShow, onReady, onHide, onLaunch, onNavigationBarButtonTap, on
 import { ref, reactive, computed, getCurrentInstance, toRefs, inject } from "vue";
 import { publicStores, useStores } from "@/store/modules/index";
 
+import chart from "./chart.vue";
+
+import { dmpProductAttribute, historyMetrics, last } from "@/api/business/fireIot/deviceSelect/index";
+
 const { proxy } = getCurrentInstance();
+const publicStore = publicStores();
 
 const dataList = ref([
   {
     title: "设备类型",
-    value: "烟感",
+    value: publicStore.$state.deviceDetailsArray.productName,
   },
   {
     title: "设备编号",
-    value: "32942389473274923",
+    value: publicStore.$state.deviceDetailsArray.deviceId,
   },
   {
     title: "物联网卡号",
-    value: "3489494234564",
+    value: publicStore.$state.deviceDetailsArray.simCode,
   },
   {
     title: "安装位置",
-    value: "上海市青浦区徐泾镇徐乐路208号",
+    value: publicStore.$state.deviceDetailsArray.installAddress,
   },
   {
     title: "添加时间",
-    value: "2020-06-01 13:06:20",
+    value: publicStore.$state.deviceDetailsArray.createdTime ? publicStore.$state.deviceDetailsArray.createdTime.replace("T", " ") : "",
   },
 ]);
 
+const checkboxDataList = ref([]); //复选框渲染数据存储
+const checkboxValueList = ref([]); //复选框值数据存储
+
+const realTimeDataList = ref([]); //实时数据存储
+const tableDataList = ref([]); //表格数据存储
+const currentDateList = ref([]); //图表数据存储
+
+const modalShow = ref(false); //模态框显示隐藏
+
+const calendar = ref(null);
+const calendarStartTime = ref(""); //日历开始时间
+const calendarEndTime = ref(""); //日历结束时间
+function open() {
+  calendar.value.open();
+}
+
+/**
+ * @初始化
+ */
+function init() {
+  dmpProductAttribute({
+    current: 1,
+    size: 100,
+    attributeName: "",
+    productId: publicStore.$state.deviceDetailsArray.productId,
+  }).then((requset) => {
+    if (requset.status === "SUCCESS") {
+      checkboxDataList.value = requset.data.records;
+      realTimeDataList.value = requset.data.records;
+
+      var array = [];
+
+      requset.data.records.forEach((el) => {
+        array.push(el.attributeCode);
+      });
+
+      last({
+        deviceId: publicStore.$state.deviceDetailsArray.deviceId,
+        metrics: array,
+      }).then((requsets) => {
+        if (requsets.status === "SUCCESS") {
+          realTimeDataList.value.forEach((el) => {
+            el.value = 0;
+            requsets.data.forEach((e) => {
+              if (el.attributeCode === e.metric) {
+                el.value = e.value;
+              }
+            });
+          });
+        }
+      });
+    }
+  });
+}
+
+/**
+ * @tabs切换change事件
+ */
+const list = ref(["实时数据", "历史数据"]);
+const tabPosition = ref(0);
+function tabPositionChange(index) {
+  tabPosition.value = index;
+}
+
+/**
+ * @checkbox选中change事件
+ */
+function checkboxChange(value) {
+  checkboxValueList.value = value;
+
+  historyMetricsApi();
+}
+
+/**
+ * @日历确认事件
+ */
+function calendarConfirm(e) {
+  calendarStartTime.value = e.range.before;
+  calendarEndTime.value = e.range.after ? e.range.after : e.range.before;
+  historyMetricsApi();
+}
+
+/**
+ * @设备多属性历史数据请求
+ * @api接口请求
+ */
+function historyMetricsApi() {
+  historyMetrics({
+    startTime: calendarStartTime.value,
+    endTime: calendarEndTime.value,
+    deviceId: publicStore.$state.deviceDetailsArray.deviceId,
+    deviceType: publicStore.$state.deviceDetailsArray.deviceType,
+    metrics: checkboxValueList.value,
+  }).then((requset) => {
+    if (requset.status === "SUCCESS") {
+      currentDateList.value = requset.data;
+
+      checkboxDataList.value.forEach((el) => {
+        currentDateList.value.forEach((e) => {
+          if (el.attributeCode == e.metric) {
+            e.attributeName = el.attributeName;
+          }
+        });
+      });
+
+      currentDateList.value.forEach((el) => {
+        if (el.metricItems.length > 0) {
+          el.data = [];
+          el.metricItems.forEach((e) => {
+            el.data.push([e.timestamp, e.value]);
+          });
+        } else {
+          el.data = [];
+        }
+      });
+    }
+  });
+}
+
 onReady(() => {});
 
 onShow(() => {
@@ -85,8 +254,14 @@ onShow(() => {
 });
 
 onLoad((options) => {
-  console.log(options.type);
+  init();
 });
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.app-subsection {
+  display: flex;
+  margin-bottom: 10px;
+  padding: 0px 5rem;
+}
+</style>

+ 15 - 9
src/pages/business/fireIot/deviceSelect/components/deviceDetailsList.vue

@@ -16,7 +16,7 @@
 
     <view class="deviceDetailsList-container">
       <view class="menu-list margin-0">
-        <view class="list-cell list-cell-arrow" v-for="(base, index) in dataList" :key="index" @click="handleToDevice(base.type)">
+        <view class="list-cell list-cell-arrow" v-for="(base, index) in dataList" :key="index" @click="handleToDevice(base)">
           <view class="menu-item-box">
             <view class="title">{{ base.deviceName }}</view>
           </view>
@@ -36,12 +36,14 @@ import { publicStores, useStores } from "@/store/modules/index";
 import { dmpDeviceInfo } from "@/api/business/fireIot/deviceSelect/index";
 
 const { proxy } = getCurrentInstance();
+const publicStore = publicStores();
 
 const dataList = ref([]);
 
 const show = ref(false);
 const deviceName = ref("");
 const productId = ref("");
+const productName = ref("");
 const pageSize = ref(20);
 const current = ref(1);
 const total = ref(0);
@@ -52,14 +54,12 @@ const total = ref(0);
 function init() {
   dmpDeviceInfo({ productId: productId.value, deviceName: deviceName.value, current: current.value, size: pageSize.value }).then((requset) => {
     if (requset.status === "SUCCESS") {
-      if (requset.data.records.length > 0) {
-        uni.setNavigationBarTitle({
-          title: `${requset.data.records[0].productName}(${requset.data.total})`,
-        });
-      }
-
       dataList.value = requset.data.records;
       total.value = requset.data.total;
+
+      uni.setNavigationBarTitle({
+        title: `${productName.value}(${total.value})`,
+      });
     }
   });
 }
@@ -75,8 +75,11 @@ function paginationChange(e) {
 /**
  * @设备详情跳转点击事件
  */
-function handleToDevice(type) {
-  proxy.$tab.navigateTo("/pages/business/fireIot/deviceSelect/components/deviceDetails?type=" + type);
+function handleToDevice(array) {
+  proxy.$tab.navigateTo("/pages/business/fireIot/deviceSelect/components/deviceDetails");
+
+  publicStore.$state.deviceDetailsArray = array;
+  publicStore.$state.deviceDetailsArray.productName = productName.value;
 }
 
 onReady(() => {});
@@ -87,6 +90,9 @@ onShow(() => {
 });
 
 onLoad((options) => {
+  if ("productName" in options) {
+    productName.value = options.productName;
+  }
   if ("id" in options) {
     productId.value = parseInt(options.id);
     init();

+ 3 - 3
src/pages/business/fireIot/deviceSelect/index.vue

@@ -1,7 +1,7 @@
 <template>
   <scroll-view class="bg-white scroll-height" :scroll-y="true" :data-theme="'theme-' + proxy.$settingStore.themeColor.type">
     <u-grid :border="true">
-      <u-grid-item v-for="(base, index) in dataList" :key="index" @click="handleToDevice(base.id)">
+      <u-grid-item v-for="(base, index) in dataList" :key="index" @click="handleToDevice(base.id, base.productName)">
         <u-badge type="primary" max="9999" :value="base.deviceCount" :showZero="true" :absolute="true" :offset="[10, 10, 0, 0]"></u-badge>
         <image class="margin-b-15" style="width: 40px; height: 40px; margin-top: 35px" :src="base.imagePath" mode="aspectFill"></image>
         <text class="margin-b-15 grid-text">{{ base.productName }}</text>
@@ -48,8 +48,8 @@ function init() {
   });
 }
 
-function handleToDevice(id) {
-  proxy.$tab.navigateTo("/pages/business/fireIot/deviceSelect/components/deviceDetailsList?id=" + id);
+function handleToDevice(id, productName) {
+  proxy.$tab.navigateTo(`/pages/business/fireIot/deviceSelect/components/deviceDetailsList?id=${id}&productName=${productName}`);
 }
 
 onShow(() => {

+ 7 - 3
src/static/iconfont/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 3620854 */
-  src: url('https://at.alicdn.com/t/c/font_3620854_bnae7oxag3.woff2?t=1684144663663') format('woff2'),
-       url('https://at.alicdn.com/t/c/font_3620854_bnae7oxag3.woff?t=1684144663663') format('woff'),
-       url('https://at.alicdn.com/t/c/font_3620854_bnae7oxag3.ttf?t=1684144663663') format('truetype');
+  src: url('https://at.alicdn.com/t/c/font_3620854_kea20ky3fh.woff2?t=1686283411818') format('woff2'),
+       url('https://at.alicdn.com/t/c/font_3620854_kea20ky3fh.woff?t=1686283411818') format('woff'),
+       url('https://at.alicdn.com/t/c/font_3620854_kea20ky3fh.ttf?t=1686283411818') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,10 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.ucicon-bianji1:before{
+  content: "\e602";
+}
+
 .ucicon-people:before {
   content: "\e736";
 }

BIN
src/static/iconfont/iconfont.ttf


+ 0 - 3
src/static/scss/colorui.css

@@ -10,9 +10,6 @@
 /* ==================
         初始化
  ==================== */
-* {
-  touch-action: pan-y;
-}
 body {
   background-color: #f5f6f7;
   font-size: 28upx;

+ 9 - 1
src/static/scss/index.scss

@@ -98,10 +98,18 @@
 }
 
 //margin-10
-.margin-bottom-10 {
+.margin-b-10 {
     margin-bottom: 10px
 }
 
+.margin-l-10 {
+    margin-left: 10px;
+}
+
+.margin-r-10 {
+    margin-right: 10px;
+}
+
 //margin-15
 .margin-t-15 {
     margin-top: 15px

+ 1 - 1
src/static/scss/public.scss

@@ -194,7 +194,7 @@ uni-input {
         padding: 0 10px;
         min-height: 36px;
         overflow: hidden; //超出的文本隐藏
-        // text-overflow: ellipsis; //溢出用省略号显示
+        text-overflow: ellipsis; //溢出用省略号显示
         overflow: auto;
         white-space: nowrap; // 默认不换行;
         font-size: 14px;

+ 12 - 0
src/static/scss/sidebar.scss

@@ -93,7 +93,19 @@ uni-button[type='primary'] {
 
                     .uni-calendar-item--checked {
                         @include background_color('themeColor');
+
+                    }
+
+                    .uni-calendar-item--before-checked {
+                        @include font_color('fontColor');
+                    }
+
+                    .uni-calendar-item--multiple {
+                        @include background_color('themeColor');
+                        @include font_color('fontColor');
                     }
+
+
                 }
             }
         }

+ 3 - 0
src/store/modules/public.js

@@ -127,6 +127,9 @@ const publicStore = defineStore("public", {
     ],
 
     arrayList: [], //撒点弹框数据存储
+
+
+    deviceDetailsArray: {},//设备详情页面-数据存储
   }),
   persist: {
     // 自定义数据持久化方式