瀏覽代碼

+ h5 增加指令下发

chen.cheng 7 月之前
父節點
當前提交
4a098e7bf0

+ 19 - 1
src/api/device.js

@@ -35,4 +35,22 @@ export const getDeviceService = async (data) => {
 export const deviceServiceApply = (deviceId, identifer, params) => {
   return post(`/online/device/service/request/${deviceId}/${identifer}`,
     params);
-}
+}
+
+export const getAlarmLog = ({page = Page.page, size = Page.size, ...params}) => {
+  return post(`/alarm/getAlarmLog`, {
+    page,
+    size,
+    ...params
+  });
+}
+
+export const getAlarmLogDetail = ({page = Page.page, size = Page.size, ...params}) => {
+  return post(`/alarm/findAlarmLogById`, {
+    page,
+    size,
+    ...params
+  });
+}
+
+

+ 20 - 1
src/common/consts/DeviceConst.js

@@ -27,4 +27,23 @@ export const DeviceState = {
     label: '在线',
     color: '#52c41a'
   }
-};
+};
+
+export const DeviceAlarmState = {
+  ALL: {
+    value: "",
+    label: '全部'
+  },
+  // 未处理
+  UNPROCESSED: {
+    value: 1,
+    label: '未处理',
+    color: '#f5222d'
+  },
+  // 已处理
+  PROCESSED: {
+    value: 2,
+    label: '已处理',
+    color: '#52c41a'
+  }
+}

+ 7 - 0
src/pages.json

@@ -60,6 +60,13 @@
           }
         },
         {
+          "path": "device/alarm-list",
+          "style": {
+            "navigationBarTitleText": "设备告警",
+            "enablePullDownRefresh": true
+          }
+        },
+        {
           "path": "device/detail",
           "style": {
             "navigationBarTitleText": "详细信息"

+ 1 - 1
src/pages/workbench/appctl.vue

@@ -58,7 +58,7 @@ const ctlItems = ref([
     name: alarm,
     title: '设备告警',
     color: '#0079fe',
-    target: "/pages/workbenchsub/device/detail"
+    target: "/pages/workbenchsub/device/alarm-list"
   },
 ]);
 

+ 1 - 1
src/pages/workbench/usrsubmit.vue

@@ -2,7 +2,7 @@
   <panel>
     <template #header>
       <up-icon size="45rpx" :name="tag"></up-icon>
-      我的提报
+      我的工单
       <up-text
           color="#007aff"
           size="24rpx"

+ 514 - 0
src/pages/workbenchsub/device/alarm-detail.vue

@@ -0,0 +1,514 @@
+<template>
+  <view class="device-info-wrap info-wrap">
+    <panel>
+      <template v-slot:header>
+        <up-icon size="28rpx" :name="list"></up-icon>
+        告警信息
+        <up-tag
+            :text="state.deviceInfo.statusName"
+            plain
+            size="mini"
+            :bgColor="valueToConst(DeviceState,state.deviceInfo.status).color"
+            class="tag-stat"/>
+      </template>
+      <template v-slot:content>
+        <view class="order-info" style="width: 100%">
+          <view class="mix-wrap">
+            <view class="info-column">
+              <label-text
+                  label="所属产品"
+                  :text="formatTxt(state.deviceInfo.productName)"
+              />
+              <label-text
+                  label="设备编码"
+                  :line="2"
+                  :text="formatTxt(state.deviceInfo.name)"
+              />
+              <label-text
+                  label="设备类型"
+                  :text="formatTxt(state.deviceInfo.deviceTypeName)"
+              />
+              <label-text
+                  label="设备厂家"
+                  :text="formatTxt(state.deviceInfo.manufactor)"
+              />
+            </view>
+            <u--image
+                :showLoading="true"
+                :src="getDevImg(state.deviceInfo.devImg)"
+                width="160rpx"
+                height="150rpx"
+            />
+          </view>
+          <view class="line-info-wrap">
+            <label-text
+                label="第三方设备编码"
+                :text="formatTxt(state.deviceInfo.thirdDevCode)"
+            />
+            <label-text
+                label="最新在线时间"
+                :text="formatTxt(state.deviceInfo.lastOnlineTime)"
+            />
+            <label-text
+                label="最后通讯时间"
+                :text="formatTxt(state.deviceInfo.lastReportTime)"
+            />
+            <label-text
+                label="位置"
+                :line="2"
+                :text="formatTxt(state.deviceInfo.locationStr)"
+            />
+            <label-text
+                label="描述"
+                :line="3"
+                :text="formatTxt(state.deviceInfo.description)"
+            />
+          </view>
+        </view>
+      </template>
+    </panel>
+    <panel>
+      <template v-slot:header>
+        <up-icon size="28rpx" :name="list"></up-icon>
+        设备调试
+      </template>
+      <template v-slot:content>
+        <up-cell-group :border="false">
+          <up-cell title="选择服务" :isLink="true" arrow-direction="left-arrow" @click="state.serviceShow = true">
+            <template v-slot:value>
+              <up-text v-if="state.selectedService" :lines="1" align="right" :text="state.selectedService.name"/>
+            </template>
+          </up-cell>
+          <up-cell
+              v-if="state.selectedService && state.selectedService.inputData"
+              v-for="item in state.selectedService.inputData"
+              :key="item.identifier"
+              :title="item.name"
+              :isLink="false"
+          >
+            <template #value>
+              <up-input
+                  :placeholder="`请输入${item.name}`"
+                  v-model="state.orderInfo[item.identifier]"
+              ></up-input>
+            </template>
+          </up-cell>
+          <up-cell :border="false">
+            <template v-slot:value>
+              <up-button type="primary" size="small" text="下发指令" @click="onCommand"></up-button>
+            </template>
+          </up-cell>
+        </up-cell-group>
+      </template>
+    </panel>
+    <panel>
+      <template v-slot:header>
+        <up-icon size="28rpx" :name="list"></up-icon>
+        运行状态
+      </template>
+      <template v-slot:content>
+        <up-cell-group :border="false">
+          <up-cell
+              v-for="(item,index) in state.metaInfo.dataMetaRunInfo"
+              :title="`${item.name}(${item.identifier})`"
+              :key="index"
+              size="small"
+          >
+            <template v-slot:label>
+              <view class="meta-data">
+                <up-text
+                    :lines="1"
+                    :text="`${item.data}${item.data && JSON.parse(item.dataType).specs.unit ? JSON.parse(item.dataType).specs.unit:''}`"
+                    size="30rpx"
+                    :bold="true"
+                >
+                </up-text>
+                <up-text
+                    :lines="1"
+                    :text="formatTxt(item.timeStamp)"
+                    size="20rpx"
+                >
+                </up-text>
+              </view>
+            </template>
+          </up-cell>
+        </up-cell-group>
+      </template>
+    </panel>
+    <up-tabbar
+        :fixed="true"
+        :placeholder="false"
+        :safeAreaInsetBottom="false"
+    >
+      <view class="warning-btn" @click="state.handleForm=true">报废</view>
+      <view class="def-btn"
+            v-if="DeviceState.UNACTIVATED.value !== state.deviceInfo.status"
+            @click="state.showDisableModal = true">禁用
+      </view>
+      <view
+          class="def-btn"
+          v-if="DeviceState.UNACTIVATED.value === state.deviceInfo.status"
+          @click="state.showEnableModal = true"
+      >激活
+      </view>
+    </up-tabbar>
+    <up-action-sheet
+        :show="state.handleForm"
+        @close="state.handleForm = false"
+        title="报废设备"
+        :round="10"
+    >
+      <view class="action-content common-form">
+        <up-form
+            labelPosition="left"
+            :model="state.model"
+            :rules="state.rules"
+            ref="formRef"
+            labelWidth="120"
+        >
+          <up-form-item
+              label="设备名称"
+              borderBottom
+              :required="true"
+          >
+            <up-input
+                :modelValue="state.deviceInfo.deviceNickname"
+                border="none"
+                disabled
+            ></up-input>
+          </up-form-item>
+          <up-form-item
+              label="设备编码"
+              borderBottom
+              :required="true"
+          >
+            <up-input
+                :modelValue="state.deviceInfo.name"
+                border="none"
+                disabled
+            ></up-input>
+          </up-form-item>
+
+          <up-form-item
+              label="报废时间"
+              prop="order.startDate"
+              borderBottom
+              :required="true"
+              @click="state.showStartClendar = true; hideKeyboard()"
+          >
+            <up-input
+                :modelValue="timestampToDate(state.model.order.startDate)"
+                disabled
+                disabledColor="#ffffff"
+                placeholder="请选择预计开始日期"
+                border="none"
+            ></up-input>
+            <template #right>
+              <up-icon
+                  name="arrow-right"
+              ></up-icon>
+            </template>
+          </up-form-item>
+          <up-form-item
+              label="报废原因"
+              borderBottom
+          >
+            <up-textarea
+                placeholder="报废原因"
+                v-model="state.model.order.desc"
+            ></up-textarea>
+          </up-form-item>
+          <up-button @click="submit" style="margin-top: 20rpx;">提交</up-button>
+        </up-form>
+      </view>
+    </up-action-sheet>
+    <up-action-sheet
+        :actions="state.serviceList"
+        @select="selectClick"
+        title="选择服务"
+        :show="state.serviceShow"
+        @close="state.serviceShow = false"
+        :closeOnClickOverlay="true"
+    />
+    <up-modal
+        :show="state.showDisableModal"
+        title="禁用设备"
+        :closeOnClickOverlay="true"
+        :showCancelButton="true"
+        @close="state.showDisableModal = false"
+        @cancel="state.showDisableModal = false"
+        @confirm="onConfirmDisable"
+    >
+      <view class="modal-content">
+        设备禁用后将导致该设备无法连接
+      </view>
+    </up-modal>
+    <up-modal
+        :show="state.showEnableModal"
+        title="启用设备"
+        :closeOnClickOverlay="true"
+        :showCancelButton="true"
+        @close="state.showEnableModal = false"
+        @cancel="state.showEnableModal = false"
+        @confirm="onConfirmEnable"
+    >
+      <view class="modal-content">
+        设备禁用后将导致该设备无法连接
+      </view>
+    </up-modal>
+    <up-datetime-picker
+        :show="state.showStartClendar"
+        v-model="state.model.order.startDate"
+        mode="datetime"
+        @close="state.showStartClendar = false"
+        :closeOnClickOverlay="true"
+        @confirm="(e)=>confirm(e,'showStartClendar')"
+        @cancel="state.showStartClendar = false"
+    ></up-datetime-picker>
+    <up-toast ref="uToastRef"></up-toast>
+  </view>
+</template>
+
+<script setup lang="ts">
+
+import list from "@/static/aiot/list.svg";
+import Panel from "@/components/pannel/index.vue";
+import LabelText from "@/components/labeltext/index.vue";
+import {onMounted, reactive, ref} from 'vue';
+import {
+  deviceServiceApply,
+  forbidDevice,
+  getDeviceDetail,
+  getDeviceMeta,
+  getDeviceService,
+  scrapDevice,
+  startDevice
+} from "@/api/device.js";
+import {onLoad} from "@dcloudio/uni-app";
+import {DateFormat, formatTxt, getDevImg, hideKeyboard, reloadPage, timestampToDate} from "@/util/index.js";
+import {valueToConst} from "@/common/consts/CommonConst.js";
+import {DeviceState} from "@/common/consts/DeviceConst.js";
+import dayjs from "dayjs";
+
+const uToastRef = ref(null)
+const state = reactive({
+  handleForm: false,
+  showDisableModal: false,
+  showEnableModal: false,
+  deviceInfo: {},
+  serviceShow: false,
+  metaInfo: {
+    dataMetaRunInfo: [],
+    dataOnlineRunInfo: [],
+  },
+  model: {
+    order: {
+      startDate: dayjs().valueOf(),
+      desc: '',
+    },
+  },
+  rules: {
+    'order.startDate': [
+      {required: true, message: '请选择报废日期'},
+    ],
+  },
+  serviceDic: {},
+  serviceList: [],
+  selectedService: null,
+  orderInfo: {}
+});
+const deviceId = ref('')
+onLoad((option) => {
+  deviceId.value = option.id
+});
+onMounted(() => {
+  getDeviceDetail(deviceId.value).then((data) => {
+    state.deviceInfo = data
+
+    if (data.metadataId) {
+      qryDeviceService(data.metadataId);
+    }
+  })
+  getDeviceMeta(deviceId.value).then((res) => {
+    if (res.metadataList) {
+      state.metaInfo.dataMetaRunInfo = res.metadataList.filter((item) => item.disply !== false);
+    }
+    if (res.onlineList.type) {
+      state.metaInfo.dataOnlineRunInfo = res.onlineList
+    }
+  })
+
+});
+const onConfirmDisable = () => {
+  forbidDevice([deviceId.value]).then(() => {
+    uni.showToast({title: '操作成功', icon: "success"});
+    state.showDisableModal = false;
+  })
+};
+const qryDeviceService = (metaId) => {
+  getDeviceService({
+    metadataId: metaId
+  }).then((res) => {
+    if (res && res.length > 0) {
+      state.serviceList = res.map((item) => {
+        const {identifier, inputData, name} = item;
+        const inputDataFormat = JSON.parse(inputData);
+        state.serviceDic[identifier] = {
+          identifier,
+          name,
+          inputData: inputDataFormat
+        }
+        return {
+          name: name,
+          value: identifier,
+          inputData: inputDataFormat
+        }
+      })
+    }
+  })
+}
+const onConfirmEnable = () => {
+  state.showEnableModal = false;
+  startDevice([deviceId.value]).then(() => {
+    uni.showToast({title: '操作成功', icon: "success"});
+    state.showEnableModal = false;
+  })
+}
+const confirm = (e, type) => {
+  state[type] = false;
+};
+const submit = () => {
+  hideKeyboard()
+  let formData = {
+    id: deviceId.value,
+    isScrap: true,
+    scrapReason: state.model.order.desc,
+    scrapTimeStr: timestampToDate(state.model.order.startDate, DateFormat.YYYYMMDDHHMMSS),
+  };
+
+  scrapDevice(formData).then(() => {
+    state.handleForm = false;
+    uToastRef.value.show({
+      type: 'default',
+      message: "操作成功",
+      complete() {
+        uni.$emit('refreshData', {refresh: true});
+        uni.navigateBack({
+          delta: 1
+        })
+      }
+    });
+  })
+}
+const selectClick = (e) => {
+  state.serviceShow = false;
+  state.selectedService = e;
+  state.orderInfo = {};
+}
+
+const onCommand = () => {
+  if (!state.orderInfo) {
+    uToastRef.value.show({
+      type: 'error',
+      message: "请输入指令",
+    });
+    return
+  }
+  deviceServiceApply(deviceId.value, state.selectedService.value, state.orderInfo).then(() => {
+    uToastRef.value.show({
+      type: 'default',
+      message: "操作成功",
+      complete() {
+        reloadPage();
+      }
+    });
+  })
+}
+</script>
+
+
+<style lang="scss">
+.device-info-wrap {
+  padding-bottom: 140rpx;
+
+  .panel-wrap {
+    margin-bottom: $uni-block-gap;
+  }
+
+  .mix-wrap {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    .info-column {
+      display: flex;
+      flex: 1;
+      align-items: flex-start;
+      justify-content: flex-start;
+      flex-direction: column;
+    }
+  }
+
+  .panel-content {
+    box-sizing: border-box;
+    padding: 15px 15px 15px 15px;
+  }
+
+  .label-text-wrap {
+    margin-top: $uni-text-gap;
+  }
+
+  .stepper-container {
+    .title-date {
+      margin-left: auto;
+    }
+  }
+
+  .u-tabbar__content__item-wrapper {
+    padding-bottom: 40rpx;
+
+
+    > view {
+      box-sizing: border-box;
+      flex: 1;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      color: $uni-bg-color;
+
+      &:not(:last-child) {
+        border-right: 2rpx solid #fefefe47;
+      }
+    }
+
+    .warning-btn {
+      background-color: $uni-color-error;
+    }
+
+    .def-btn {
+      background-color: $uni-color-primary;
+    }
+  }
+
+  .u-cell-group {
+    .u-cell {
+      margin-bottom: $uni-text-gap;
+
+      .u-cell__body {
+        padding: 0;
+        min-height: 70rpx;
+      }
+    }
+  }
+
+  .meta-data {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  .action-content {
+    padding: 0 $uni-pop-padding;
+  }
+}
+</style>

+ 206 - 0
src/pages/workbenchsub/device/alarm-list.vue

@@ -0,0 +1,206 @@
+<template>
+  <view class="device-list">
+    <up-search v-model="searchValue"></up-search>
+    <view class="stat-ctl">
+      <view class="u-page__tag-item" v-for="(item, index) in DeviceAlarmState" :key="index">
+        <up-tag
+            :text="item.label"
+            :plain="selectTag === item.value"
+            :plainFill="true"
+            bgColor="#007aff"
+            :name="item.value"
+            @click="()=>radioClick(item)"
+        >
+        </up-tag>
+      </view>
+    </view>
+    <view class="u-page-list" style="padding-bottom: 100rpx">
+      <up-list @scrolltolower="scrolltolower">
+        <up-list-item
+            v-for="(item, index) in indexList"
+            :key="item.id"
+        >
+          <view class="item-content">
+            <up-image
+                :showLoading="true"
+                :src="getDevImg(item.devImg)"
+                width="160rpx"
+                height="150rpx"
+                :lazy-load="true"
+            >
+              <template #error>
+                <view style="font-size: 24rpx;">加载失败</view>
+              </template>
+            </up-image>
+            <view class="content-info">
+              <view>
+                <up-text :lines="1" :text="`设备名称: ${formatTxt(item.alarmName)}`" size="24rpx"/>
+                <up-tag
+                    :text="valueToConst(DeviceAlarmState,item.handleStatus).label"
+                    plain
+                    size="mini"
+                    :bgColor="valueToConst(DeviceAlarmState,item.handleStatus).color"
+                    class="tag-stat"
+                ></up-tag>
+                <up-text text="详情>" color="#007aff" style="flex:none;width: auto"
+                         @click="()=>onDetailClick(item)"></up-text>
+              </view>
+              <view>
+                <up-text :lines="1" :text="`设备名称:${formatTxt(item.deviceName)}`" size="24rpx"/>
+              </view>
+              <view>
+                <up-text :lines="1" :text="`上报时间:${formatTxt(item.time)}`" size="24rpx"/>
+              </view>
+            </view>
+          </view>
+        </up-list-item>
+        <up-loading-icon v-if="loading" text="加载中" textSize="12"></up-loading-icon>
+        <up-text
+            v-if="!loadmoreFlag" text="已经到底了" style="justify-content: center;"
+            size="24rpx"
+            color="gray"
+        >
+        </up-text>
+      </up-list>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import {ref} from "vue"
+import {DeviceAlarmState} from '@/common/consts/DeviceConst.js'
+import {valueToConst} from '@/common/consts/CommonConst.js'
+import {onLoad, onPullDownRefresh, onShow} from '@dcloudio/uni-app';
+import {formatTxt, getDevImg, navigateTo,} from '@/util/index.js'
+import {getAlarmLog} from "@/api/device.js";
+
+let searchValue = ref("")
+let indexList = ref([])
+let selectTag = ref(DeviceAlarmState.ALL.value)
+let loadmoreFlag = ref(true)
+let loading = ref(false)
+const page = ref(1)
+
+onLoad(() => {
+  page.value = 1
+  loading.value = true;
+  loadmore();
+});
+onPullDownRefresh(() => {
+  refreshPage(() => {
+    uni.stopPullDownRefresh();
+  })
+})
+
+onShow(() => {
+  uni.$on('refreshData', (res) => {
+    if (res.refresh) {
+      refreshPage()
+    }
+  })
+});
+
+const refreshPage = (callback = () => {
+}) => {
+  page.value = 1
+  indexList.value = []
+  loading.value = true;
+  loadmore(callback);
+}
+
+function radioClick(item) {
+  selectTag.value = item.value
+  page.value = 1
+  indexList.value = []
+  loading.value = true;
+  loadmore();
+}
+
+
+const loadmore = async (callback = null) => {
+  const {records} = await getAlarmLog({
+    page: page.value,
+    status: selectTag.value,
+  })
+  if (records && records.length > 0) {
+    indexList.value = indexList.value.concat(records)
+    page.value += 1
+    if (records.length < 10) {
+      loadmoreFlag.value = false;
+    }
+  } else {
+    loadmoreFlag.value = false;
+  }
+  loading.value = false;
+  callback && callback();
+};
+
+const scrolltolower = () => {
+  if (!loadmoreFlag.value) return;
+  loading.value = true;
+  loadmore();
+};
+const onDetailClick = (item) => {
+  navigateTo({
+    url: "/pages/workbenchsub/device/detail",
+    param: item
+  })
+}
+</script>
+
+<style lang="scss">
+.device-list {
+  width: 680rpx;
+  margin: 14rpx auto 0;
+
+  .u-search__content {
+    background: $uni-bg-color !important;
+
+    .u-search__content__input {
+      background-color: $uni-bg-color !important;
+    }
+  }
+
+  .u-page-list {
+    margin-top: 14rpx;
+  }
+
+  .item-content {
+    width: 100%;
+    border-radius: $comm-border-radius;
+    display: flex;
+    box-sizing: border-box;
+    align-items: center;
+    justify-content: flex-start;
+    background: $uni-bg-color;
+    margin-bottom: 14rpx;
+
+    padding: 14rpx;
+
+    .content-info {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      align-items: flex-start;
+      justify-content: flex-start;
+      margin-left: 14rpx;
+
+      > view {
+        font-size: inherit;
+        width: 100%;
+        display: flex;
+        align-items: center;
+        justify-content: flex-start;
+
+        .u-link {
+          flex: none;
+        }
+
+        &:not(:first-child) {
+          margin-top: 14rpx;
+        }
+      }
+    }
+  }
+}
+</style>