Commit 19bc7b16c288841054b8807daa384631d6359210

Authored by xp.Huang
2 parents 32c068e5 ab30c83c

Merge branch 'main_dev'

# Conflicts:
#	pages/alarm/alarm.vue
Showing 47 changed files with 2581 additions and 1944 deletions

Too many changes to show.

To preserve performance only 47 of 106 files are displayed.

1 1 <script>
2 2 import store from '@/store';
3   - // import {
4   - // scene
5   - // } from '@/config/common';
6 3
7 4 export default {
8 5 //设置全局变量,解绑时从这里取
... ... @@ -10,27 +7,12 @@
10 7 openId: ''
11 8 },
12 9 onLaunch(e) {
13   - //取出缓存数据
14   - // store.commit('setCacheData');
15   - //获取二维码信息
16   - // scene(e);
17 10 // #ifdef APP-PLUS
18 11 uni.reLaunch({
19 12 url: '/pages/index/splash'
20 13 })
21 14 // #endif
22 15 },
23   - onShow(e) {
24   - // #ifdef MP
25   - // mpUpData(); //检测小程序更新
26   - // #endif
27   - // #ifdef APP-PLUS
28   - // uni.reLaunch({
29   - // url: '/pages/index/splash'
30   - // })
31   - // #endif
32   - },
33   - onHide() {},
34 16 onUnload(){
35 17 uni.setStorageSync('getConfiguration', {
36 18 isConfiguration: false
... ...
  1 +<template>
  2 + <view class="alarm-detail-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <view class="alarm-detail-column">
  6 + <view class="u-flex detail-column">
  7 + <view class="column-line" v-for="(item,index) in alarmDetail" :key="index">
  8 + <view class="column">
  9 + <text class="text-org-bold">{{item.label}}</text>
  10 + <text class="text-device-muted text-clip alarm-text"
  11 + :style="{color:hasColor.includes(item.label)?'#DE4437':''}">
  12 + {{item.label===hasColor[0]? setAlarmStatus(item.value):item.label===hasColor[1]?setAlarmSeverity(item.value):
  13 + item.label==='触发值:'?formatAlarmValueText:item.label==='触发条件:'?formatAlarmConditionText
  14 + :item.label==='触发属性:'?formatAttrText:item.label==='告警设备:'?formatDeviceText:item.value}}
  15 + </text>
  16 + </view>
  17 + <view class="bottom-line"></view>
  18 + </view>
  19 + </view>
  20 + </view>
  21 + <!-- #ifdef MP -->
  22 + <view class="handle-result text-org-bold" style="">处理结果</view>
  23 + <view class="hanle-main">
  24 + <u--form :label-style="{ 'font-size': '0rpx' }" style="padding-left: 26rpx;" labelPosition="left"
  25 + :model="formModel" ref="form1">
  26 + <u-form-item label="." prop="result" ref="item3">
  27 + <view style="position: relative;left: -60rpx;">
  28 + <u--textarea border="none" height="96" placeholder="请输入处理结果" v-model="formModel.result" count>
  29 + </u--textarea>
  30 + </view>
  31 + </u-form-item>
  32 + </u--form>
  33 + </view>
  34 + <!-- #endif -->
  35 + <!-- #ifdef APP-PLUS -->
  36 + <view class="handle-result text-org-bold">处理结果</view>
  37 + <view class="hanle-main">
  38 + <view>
  39 + <u--textarea border="none" height="96" placeholder="请输入处理结果" v-model="formModel.result" count>
  40 + </u--textarea>
  41 + </view>
  42 + </view>
  43 + <!-- #endif -->
  44 + <view class="bottom-button">
  45 + <view v-if="handleText.includes(alarmDetail[8].value)" class="u-flex button-item">
  46 + <u-button @click="handleSubmit" type="primary" shape="circle" text="处理"></u-button>
  47 + </view>
  48 + <view v-if="clearText.includes(alarmDetail[8].value)" class="u-flex button-item">
  49 + <u-button @click="handleRemove" type="error" shape="circle" text="清除"></u-button>
  50 + </view>
  51 + </view>
  52 + </view>
  53 +</template>
  54 +
  55 +<script>
  56 + import {
  57 + mapActions
  58 + } from 'vuex'
  59 + import api from '@/api/index.js'
  60 + import {
  61 + alarmSeverity,
  62 + alarmStatus,
  63 + operationNumberOrDate,
  64 + operationString,
  65 + operationBoolean
  66 + } from '@/pages/alarm/config/data.js';
  67 + import {
  68 + useShowToast,
  69 + useNavigateBack
  70 + } from '@/plugins/utils.js'
  71 +
  72 + export default {
  73 + data() {
  74 + return {
  75 + handleText: ['ACTIVE_UNACK', 'CLEARED_UNACK'],
  76 + clearText: [ 'ACTIVE_ACK'],
  77 + hasColor: ['告警级别:', '告警状态:'],
  78 + alarmSeverity,
  79 + alarmStatus,
  80 + operationNumberOrDate,
  81 + operationString,
  82 + operationBoolean,
  83 + formModel: {
  84 + result: ''
  85 + },
  86 + detailId: '',
  87 + alarmDetail: [],
  88 + formatDeviceText: '',
  89 + formatAlarmValueText: '',
  90 + formatAlarmConditionText: '',
  91 + formatAttrText:'',
  92 + };
  93 + },
  94 + async onLoad(e) {
  95 + if (e.data !== null) {
  96 + let params = JSON.parse(decodeURIComponent(e.data));
  97 + const {deviceName,severity,organizationName,details,type,createdTime,status,id} = params
  98 +
  99 + this.detailId = id
  100 + this.alarmDetail = [{label: '告警场景:',value: type},{label: '告警级别:',value: severity},{label: '所属组织:',value: organizationName},{label: '告警设备:',value: ''},{label:'触发属性:',value:''},{label: '触发条件:',value: ''},{label: '触发值:',value: ''},{label: '告警时间:',value: createdTime},{label: '告警状态:',value: status},]
  101 +
  102 + const keys = Object.keys(details)
  103 + const dataFormat = await this.handleAlarmDetailFormat(keys);
  104 + this.formatAlarmDevice(details,dataFormat)
  105 + this.formatAlarmValue(details,dataFormat)
  106 + this.formatAlarmCondition(details,dataFormat)
  107 + this.formatAttr(details,dataFormat)
  108 + }
  109 + // 隐藏原生的tabbar
  110 + uni.hideTabBar();
  111 + },
  112 + methods: {
  113 + ...mapActions(['updateBadgeTotal']),
  114 + setAlarmStatus(value) {
  115 + return this.alarmSeverity.find(item => item.value === value).label
  116 + },
  117 + setAlarmSeverity(value) {
  118 + return this.alarmStatus.find(item => item.value === value).label
  119 + },
  120 + returnPrevPage(title) {
  121 + useShowToast(title)
  122 + let pages = getCurrentPages(); //获取所有页面栈实例列表
  123 + let nowPage = pages[pages.length - 1]; //当前页页面实例
  124 + let prevPage = pages[pages.length - 2]; //上一页页面实例
  125 + prevPage.$vm.detailStatus = true;
  126 + },
  127 + async handleSubmit() {
  128 + if (this.formModel.result == '') return uni.$u.toast('请输入处理结果');
  129 + const res = await api.alarmApi.postAlarmAckApi(this.detailId)
  130 + if (res == '') {
  131 + this.returnPrevPage('处理成功~')
  132 + setTimeout(() => {
  133 + useNavigateBack(1)
  134 + }, 500);
  135 + }
  136 + },
  137 + async handleRemove() {
  138 + const res = await api.alarmApi.postAlarmClearApi(this.detailId)
  139 + if (res == '') {
  140 + this.returnPrevPage('清除成功~')
  141 + setTimeout(async () => {
  142 + useNavigateBack(1)
  143 + const res = await uni.$u.http.get('/yt/homepage/app?login=true');
  144 + if (res) {
  145 + //异步实时更新告警徽标数
  146 + await this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
  147 + }
  148 + }, 500);
  149 + }
  150 + },
  151 + //触发值处理
  152 + formatAlarmValue(e,dataFormat) {
  153 + const keys = Object.keys(e)
  154 + const values = keys.reduce((acc, curr) => {
  155 + const items = e[curr]?.triggerData
  156 + dataFormat.forEach((dataItem => {
  157 + if (dataItem.tbDeviceId === curr) {
  158 + if(!items?.realValue) return
  159 + acc.push(
  160 + `${items.realValue}`
  161 + )
  162 + }
  163 + }))
  164 + return acc
  165 + }, [])
  166 + this.formatAlarmValueText = values.join(',')
  167 + },
  168 + //触发条件处理
  169 + formatAlarmCondition(e) {
  170 + const keys = Object.keys(e)
  171 + const values = keys.reduce((acc, curr) => {
  172 + acc.push({
  173 + login: e[curr]?.triggerData?.logic,
  174 + logicValue: e[curr]?.triggerData?.logicValue
  175 + })
  176 + return acc
  177 + }, [])
  178 + const operation = [...operationNumberOrDate, ...operationString, ...operationBoolean]
  179 + const format = values.map(item => {
  180 + const findOperation = operation.find(findItem => findItem.value === item.login)?.symbol
  181 + return findOperation + item.logicValue
  182 + })
  183 + this.formatAlarmConditionText = format.filter(Boolean).join(',')
  184 + },
  185 + // 触发属性
  186 + formatAttr(e,dataFormat){
  187 + const keys = Object.keys(e)
  188 + const values = keys.reduce((acc, curr) => {
  189 + const items = e[curr]?.triggerData
  190 + dataFormat.forEach((dataItem => {
  191 + if (dataItem.tbDeviceId === curr) {
  192 + const findAttribute = dataItem.attribute.find(findItem => findItem.identifier === items?.key)
  193 + if(!findAttribute?.name && !items?.key) return
  194 + acc.push(`${findAttribute?.name || items?.key || ' '}`)
  195 + }
  196 + }))
  197 + return acc
  198 + }, [])
  199 + this.formatAttrText = values.join(',')
  200 + },
  201 + //告警设备处理
  202 + async formatAlarmDevice(e,dataFormat) {
  203 + if (!dataFormat) this.formatDeviceText = ''
  204 + if (Array.isArray(dataFormat) && dataFormat.length === 0) this.formatDeviceText = ''
  205 + this.formatDeviceText = dataFormat.map(item => item.name).join(',')
  206 + },
  207 + async handleAlarmDetailFormat(keys) {
  208 + const temp = [];
  209 + for (let item of keys) {
  210 + if (item === 'key' || item === 'data') return; //旧数据则终止
  211 + const deviceDetailRes = await api.deviceApi.getDeviceDetail(item);
  212 + const { deviceProfileId } = deviceDetailRes;
  213 + if (!deviceProfileId) return;
  214 + const attributeRes = await api.deviceApi.getAttribute(deviceProfileId);
  215 + const dataFormat = this.handleDataFormat(deviceDetailRes, attributeRes);
  216 + temp.push(dataFormat);
  217 + }
  218 + return temp;
  219 + },
  220 + handleDataFormat(deviceDetail, attributes) {
  221 + const { name,tbDeviceId } = deviceDetail;
  222 + const attribute = attributes.map((item) => ({
  223 + identifier: item.identifier,
  224 + name: item.name,
  225 + detail: item.detail
  226 + }));
  227 + return {
  228 + name,
  229 + tbDeviceId,
  230 + attribute,
  231 + };
  232 + }
  233 + }
  234 + };
  235 +</script>
  236 +
  237 +<style lang="scss" scoped>
  238 + @import './static/alarmDetail.scss';
  239 +
  240 + /deep/ .u-button--primary {
  241 + background-color: #377dff !important;
  242 + border-color: #377dff !important;
  243 + }
  244 +</style>
... ...
alarm-subpackage/alarm-detail/static/alarmDetail.scss renamed from alarmSubPage/alarmDetailPage/static/alarmDetail.scss
... ... @@ -6,57 +6,67 @@
6 6 .alarm-detail-column {
7 7 border-radius: 10px;
8 8 width: 688rpx;
9   - height: 573rpx;
  9 + height: 780rpx;
10 10 background-color: #ffffff;
11 11 .detail-column {
12   - height: 573rpx;
13   - justify-content: space-between;
  12 + width:750rpx;
14 13 flex-direction: column;
15 14 align-items: center;
16   - .column {
17   - flex-direction: row;
  15 + .column-line{
18 16 display:flex;
19   - // justify-content: space-between;
20   - margin-top: 10rpx;
21   - line-height: 68rpx;
22   - width: 614rpx;
23   - height: 90rpx;
24   - text-align: left;
25   - border-bottom: 0.1rpx solid #f0f0f0;
26   - .device-name{
27   - width:222rpx;
28   - }
29   - .text {
30   - color: #333333;
31   - font-size: 15px;
32   - }
33   - .image {
34   - width: 30rpx;
35   - height: 30rpx;
36   - }
37   - .text-alarm-level {
38   - color: #333333;
39   - font-size: 14px;
40   - }
41   - .text-alarm-level-lg {
42   - color: #333333;
43   - font-size: 15px;
44   - }
45   - .text-alarm-lg {
46   - color: #666666;
47   - font-size: 14px;
48   - }
49   - .text-alarm-status {
50   - color: #de4437;
51   - font-size: 14px;
  17 + flex-direction: column;
  18 + .column {
  19 + display:flex;
  20 + margin-top: 10rpx;
  21 + line-height: 68rpx;
  22 + width:700rpx;
  23 + height: 74rpx;
  24 + align-items: center;
  25 + .device-name{
  26 + width:222rpx;
  27 + }
  28 + .alarm-text{
  29 + width:460rpx;
  30 + overflow-x: scroll;
  31 + }
  32 +
  33 + .text {
  34 + color: #333333;
  35 + font-size: 15px;
  36 + }
  37 + .image {
  38 + width: 30rpx;
  39 + height: 30rpx;
  40 + }
  41 + .text-alarm-level {
  42 + color: #333333;
  43 + font-size: 14px;
  44 + }
  45 + .text-alarm-level-lg {
  46 + color: #333333;
  47 + font-size: 15px;
  48 + }
  49 + .text-alarm-lg {
  50 + color: #666666;
  51 + font-size: 14px;
  52 + }
  53 + .text-alarm-status {
  54 + color: #de4437;
  55 + font-size: 14px;
  56 + }
52 57 }
  58 + .bottom-line{
  59 + border-bottom: 0.1rpx solid #f0f0f0;
  60 + width:650rpx;
  61 + }
53 62 }
  63 +
54 64 }
55 65 }
56 66 .handle-result {
57 67 color: #333333;
58 68 font-size: 15px;
59   - margin-top: 20rpx;
  69 + margin-top: 32rpx;
60 70 }
61 71 .hanle-main {
62 72 margin-top: 30rpx;
... ... @@ -65,3 +75,13 @@
65 75 height: 273rpx;
66 76 background-color: #ffffff;
67 77 }
  78 +.bottom-button{
  79 + margin-top: 44rpx;
  80 + display: flex;
  81 + align-items: center;
  82 + gap:40rpx;
  83 + justify-content: center;
  84 + .button-item{
  85 + width: 260rpx;
  86 + }
  87 +}
... ...
1   -<template>
2   - <view class="alarm-detail-page">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <view class="alarm-detail-column">
6   - <view class="u-flex detail-column">
7   - <view class="u-flex column">
8   - <text
9   - class="text-clip device-name text-org-bold">{{ list.deviceName == null ? '暂无数据' : list.deviceName }}</text>
10   - <image class="image" src="/static/alarm-device.png"></image>
11   - </view>
12   - <view class="column">
13   - <text class="text-org-bold ">告警级别:</text>
14   - <text class="text-device-muted" style="color:#DE4437">
15   - {{
16   - list.severity == 'CRITICAL'
17   - ? '危险'
18   - : list.severity == 'MAJOR'
19   - ? '重要'
20   - : list.severity == 'MINOR'
21   - ? '次要'
22   - : list.severity == 'WARNING'
23   - ? '警告'
24   - : '不确定'
25   - }}
26   - </text>
27   - </view>
28   - <view class="column">
29   - <text class="text-org-bold">所属组织:</text>
30   - <text
31   - class="text-device-muted">{{ list.organizationName == null ? '暂无数据' : list.organizationName }}</text>
32   - </view>
33   - <view class="column">
34   - <text class="text-org-bold">告警值:</text>
35   - <text
36   - class="text-device-muted text-clip">{{ list.details == null ? '暂无数据' : formatDetailText(list.details.data) }}</text>
37   - </view>
38   - <view class="column">
39   - <text class="text-org-bold">告警场景:</text>
40   - <text
41   - class="text-device-muted text-clip">{{ list.type == null ? '暂无数据' : list.type }}</text>
42   - </view>
43   - <view class="column">
44   - <text class="text-org-bold">告警时间:</text>
45   - <text class="text-device-muted">{{ list.createdTime }}</text>
46   - </view>
47   - <view class="column">
48   - <text class="text-org-bold">告警状态:</text>
49   - <text class="text-device-muted" style="color: #DE4437;">
50   - {{
51   - list.status == 'CLEARED_UNACK'
52   - ? '清除未确认'
53   - : list.status == 'ACTIVE_UNACK'
54   - ? '激活未确认'
55   - : list.status == 'CLEARED_ACK'
56   - ? '清除已确认'
57   - : '激活已确认'
58   - }}
59   - </text>
60   - </view>
61   - </view>
62   - </view>
63   - <!-- #ifdef MP -->
64   - <view class="handle-result text-org-bold" style="">处理结果</view>
65   - <view class="hanle-main">
66   - <u--form :label-style="{ 'font-size': '0rpx' }" style="padding-left: 26rpx;" labelPosition="left"
67   - :model="formModel" ref="form1">
68   - <u-form-item label="." prop="result" ref="item3">
69   - <view style="position: relative;left: -60rpx;">
70   - <u--textarea border="none" height="96" placeholder="请输入处理结果" v-model="formModel.result" count>
71   - </u--textarea>
72   - </view>
73   - </u-form-item>
74   - </u--form>
75   - </view>
76   - <!-- #endif -->
77   - <!-- #ifdef APP-PLUS -->
78   - <view class="handle-result text-org-bold">处理结果</view>
79   - <view class="hanle-main">
80   - <view>
81   - <u--textarea border="none" height="96" placeholder="请输入处理结果" v-model="formModel.result" count>
82   - </u--textarea>
83   - </view>
84   - </view>
85   - <!-- #endif -->
86   - <view style="margin-top: 44rpx;display: flex;align-items: center;justify-content: space-between;">
87   - <view :style="[
88   - { position: list.status !== 'CLEARED_ACK' && list.status !== 'ACTIVE_ACK' ? 'relative' : '' },
89   - { right: list.status !== 'CLEARED_ACK' && list.status !== 'ACTIVE_ACK' ? '-210rpx' : '' }
90   - ]" v-if="list.status !== 'CLEARED_ACK' && list.status !== 'ACTIVE_ACK'" class="u-flex" style="width: 260rpx">
91   - <u-button @click="handleSubmit" type="primary" shape="circle" text="处理"></u-button>
92   - </view>
93   - <view style="width: 30rpx;"></view>
94   - <view
95   - :style="[{ position: list.status == 'ACTIVE_ACK' ? 'relative' : '' }, { right: list.status == 'ACTIVE_ACK' ? '207rpx' : '' }]"
96   - v-if="list.status == 'ACTIVE_ACK'" class="u-flex" style="width: 260rpx">
97   - <u-button @click="handleRemove" type="error" shape="circle" text="清除"></u-button>
98   - </view>
99   - </view>
100   - </view>
101   -</template>
102   -
103   -<script>
104   - import {
105   - mapActions
106   - } from 'vuex'
107   - import api from '@/api/index.js'
108   -
109   - export default {
110   - data() {
111   - return {
112   - formModel: {
113   - result: ''
114   - },
115   - list: {}
116   - };
117   - },
118   - onLoad(e) {
119   - if (e.data !== null) {
120   - let params = JSON.parse(e.data);
121   - this.list = params;
122   - }
123   - // 隐藏原生的tabbar
124   - uni.hideTabBar();
125   - },
126   - methods: {
127   - //处理
128   - async handleSubmit() {
129   - if (this.formModel.result == '') return uni.$u.toast('请输入处理结果');
130   - const res = await api.alarmApi.postAlarmAckApi(this.list.id)
131   - if (res == '') {
132   - uni.showToast({
133   - title: '处理成功~',
134   - icon: 'none'
135   - });
136   - let pages = getCurrentPages(); //获取所有页面栈实例列表
137   - let nowPage = pages[pages.length - 1]; //当前页页面实例
138   - let prevPage = pages[pages.length - 2]; //上一页页面实例
139   - prevPage.$vm.detailStatus = true;
140   - setTimeout(() => {
141   - uni.navigateBack({
142   - delta: 1
143   - });
144   - }, 500);
145   - }
146   - },
147   - // 清除
148   - async handleRemove() {
149   - const res = await api.alarmApi.postAlarmClearApi(this.list.id)
150   - if (res == '') {
151   - uni.showToast({
152   - title: '清除成功~',
153   - icon: 'none'
154   - });
155   - let pages = getCurrentPages(); //获取所有页面栈实例列表
156   - let nowPage = pages[pages.length - 1]; //当前页页面实例
157   - let prevPage = pages[pages.length - 2]; //上一页页面实例
158   - prevPage.$vm.detailStatus = true;
159   - setTimeout(async () => {
160   - uni.navigateBack({
161   - delta: 1
162   - });
163   - const res = await uni.$u.http.get('/yt/homepage/app?login=true');
164   - if (res) {
165   - //异步实时更新告警徽标数
166   - await this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
167   - }
168   - }, 500);
169   - }
170   - },
171   - ...mapActions(['updateBadgeTotal']),
172   - formatDetailText(e) {
173   - //去除字符串双引号
174   - const jsonStr = JSON.stringify(e);
175   - const str = jsonStr.substring(1, jsonStr.length - 1);
176   - const newStr = str.replace(/\"/g, '');
177   - return newStr.slice(0,26);
178   - }
179   - }
180   - };
181   -</script>
182   -
183   -<style lang="scss" scoped>
184   - @import './static/alarmDetail.scss';
185   -
186   - /deep/ .u-button--primary {
187   - background-color: #377dff !important;
188   - border-color: #377dff !important;
189   - }
190   -</style>
... ... @@ -11,6 +11,30 @@ const getDeviceApi = (urlParams, data) => {
11 11 return uni.$u.http.post(`/yt/device?page=${page}&pageSize=${pageSize}`, data);
12 12 };
13 13
14   -export default {
15   - getDeviceApi
  14 +// 设备详情
  15 +const getDeviceDetail = (id) => {
  16 + return uni.$u.http.get(`/yt/device/${id}`);
  17 +};
  18 +
  19 +//设备属性
  20 +const getAttribute = (deviceProfileId) => {
  21 + return uni.$u.http.get(`/yt/device/attributes/${deviceProfileId}`);
  22 +};
  23 +
  24 +//命令下发
  25 +const issueCommand = (type, tbDeviceId, data) => {
  26 + return uni.$u.http.post(`/rpc/${type==='OneWay'?'oneway':'twoway'}/${tbDeviceId}`, data)
16 27 }
  28 +
  29 +//获取命令下发记录
  30 +const getRpcRecord = (params) => {
  31 + return uni.$u.http.get('/yt/rpc', params);
  32 +};
  33 +
  34 +export default {
  35 + getDeviceApi,
  36 + getDeviceDetail,
  37 + getAttribute,
  38 + issueCommand,
  39 + getRpcRecord
  40 +}
\ No newline at end of file
... ...
... ... @@ -20,9 +20,22 @@ const getConfigurationApi = (params = {}) => {
20 20 .get('/yt/configuration/center', params)
21 21 }
22 22
  23 +// 获取看板分页API
  24 +const getVisualBoardApi = (params) => {
  25 + return uni.$u.http.get('yt/data_board',params)
  26 +}
  27 +
  28 +//获取组织列表
  29 +const getMeOrgListApi = () => {
  30 + return uni.$u.http
  31 + .get('/yt/organization/me/list')
  32 +}
  33 +
23 34 export default {
24 35 getHomeStatisticsApi,
25 36 getCameraApi,
26 37 byCameraIdGetDetailApi,
27   - getConfigurationApi
  38 + getConfigurationApi,
  39 + getMeOrgListApi,
  40 + getVisualBoardApi
28 41 }
... ...
... ... @@ -3,12 +3,18 @@ const getPlateCustomApi = () => {
3 3 return uni.$u.http.get("/yt/app_design/get")
4 4 };
5 5
6   -//第三方账号绑定API
  6 +//第三方账号登录API
7 7 const getThirdLoginApi = (code) => {
8 8 return uni.$u.http.get(`/yt/third/login/${code}`)
9 9 };
10 10
11   -//获取个人信息API
  11 +//第三方账号绑定API
  12 +const postThirdLoginApi = (data = {}) => {
  13 + return uni.$u.http
  14 + .post("/yt/third/bind", data)
  15 +};
  16 +
  17 +//设置个人信息API
12 18 const setUserInfoApi = () => {
13 19 return uni.$u.http.get("/yt/user/me/info")
14 20 };
... ... @@ -52,6 +58,18 @@ const deleteBindApi = (data = {}) => {
52 58 return uni.$u.http.delete('/yt/third', data)
53 59 }
54 60
  61 +//获取手机验证码API
  62 +const postPhoneCodeApi = (phone, data = {}) => {
  63 + return uni.$u.http
  64 + .post(`/yt/noauth/send_login_code/${phone}`, data)
  65 +}
  66 +
  67 +//手机登录API
  68 +const postPhoneLoginApi = (data = {}) => {
  69 + return uni.$u.http
  70 + .post('/yt/auth/code/login', data)
  71 +}
  72 +
55 73 export default {
56 74 getPlateCustomApi,
57 75 getThirdLoginApi,
... ... @@ -62,5 +80,8 @@ export default {
62 80 postCodeApi,
63 81 postResetCodeApi,
64 82 postPersonalInfoApi,
65   - deleteBindApi
  83 + deleteBindApi,
  84 + postPhoneCodeApi,
  85 + postPhoneLoginApi,
  86 + postThirdLoginApi
66 87 }
... ...
  1 +<template>
  2 + <view class="header-org" @click="$emit('openOrg')">
  3 + <view class="org-item">
  4 + <view class="u-flex org-contact"><text class="text">组织关系</text></view>
  5 + <view class="u-flex org-device">
  6 + <image class="device-image" :src="imageSrc"></image>
  7 + <text class="device-text">{{title}} {{ total }}</text>
  8 + </view>
  9 + </view>
  10 + <view class="org-item">
  11 + <image class="image" src="/static/arrow-right.png"></image>
  12 + </view>
  13 + </view>
  14 +</template>
  15 +
  16 +<script>
  17 + export default {
  18 + props: {
  19 + total: Number,
  20 + imageSrc: String,
  21 + title:String
  22 + }
  23 + }
  24 +</script>
  25 +
  26 +<style lang="scss" scoped>
  27 + .header-org {
  28 + width: 750rpx;
  29 + height: 150rpx;
  30 + margin-top: 1rpx;
  31 + background-color: #fff;
  32 + display: flex;
  33 + flex-direction: row;
  34 + justify-content: space-between;
  35 + position: fixed;
  36 + // z-index: 999999;
  37 + // top: 75rpx;
  38 +
  39 + .org-item {
  40 + width: 350rpx;
  41 + height: 200rpx;
  42 +
  43 + .org-contact {
  44 + flex-direction: row;
  45 + margin-top: 26rpx;
  46 + margin-left: 15rpx;
  47 +
  48 + .text {
  49 + color: #333333;
  50 + font-size: 15px;
  51 + margin-left: 14rpx;
  52 + }
  53 + }
  54 +
  55 + .org-device {
  56 + margin-top: 23rpx;
  57 + margin-left: 15rpx;
  58 + flex-direction: row;
  59 +
  60 + .device-image {
  61 + margin-left: 14rpx;
  62 + width: 30rpx;
  63 + height: 30rpx;
  64 + }
  65 +
  66 + .device-text {
  67 + margin-left: 10rpx;
  68 + color: #666666;
  69 + font-size: 12px;
  70 + }
  71 + }
  72 +
  73 + .image {
  74 + width: 6px;
  75 + height: 10px;
  76 + float: right;
  77 + margin-right: 34rpx;
  78 + margin-top: 37rpx;
  79 + }
  80 + }
  81 + }
  82 +</style>
... ...
  1 +<template>
  2 + <u-sticky>
  3 + <view class="device-top">
  4 + <view class="search">
  5 + <view>
  6 + <view class="search-left">
  7 + <slot></slot>
  8 + </view>
  9 + </view>
  10 + <view @click="$emit('openSearchDialog')" class="search-right">
  11 + <text>筛选</text>
  12 + <image src="../../static/shaixuan.png" />
  13 + </view>
  14 + </view>
  15 + <u-line />
  16 + <view class="org">
  17 + <u-cell @click="$emit('openOrg')" isLink title="组织关系" :border="false">
  18 + <view slot="label" class="label" style="display: flex; align-items: center;margin-top: 20rpx;">
  19 + <image src="../../static/org.png" style="width: 24rpx;height: 28rpx;"></image>
  20 + <view style="margin-left: 10rpx; color: #666;">
  21 + {{totalText}}
  22 + <text style="margin-left: 20rpx;">{{ total }}</text>
  23 + </view>
  24 + </view>
  25 + </u-cell>
  26 + </view>
  27 + </view>
  28 + </u-sticky>
  29 +</template>
  30 +
  31 +<script>
  32 + export default {
  33 + props: {
  34 + total: Number,
  35 + totalText: [Number, String]
  36 + }
  37 + }
  38 +</script>
  39 +
  40 +<style lang="scss" scoped>
  41 + /deep/ .u-button--primary {
  42 + background-color: #377dff !important;
  43 + border-color: #377dff !important;
  44 + }
  45 +
  46 + /deep/ .u-button--info {
  47 + background-color: #e3e3e5 !important;
  48 + border-color: #e3e3e5 !important;
  49 + }
  50 +
  51 + /deep/ .u-cell__right-icon-wrap {
  52 + margin-top: -55rpx !important;
  53 + }
  54 +
  55 + /deep/ .uni-calendar--fixed {
  56 + bottom: 172rpx !important;
  57 + }
  58 +
  59 + .pop-no-scroll {
  60 + overflow: hidden;
  61 + position: fixed;
  62 + height: 100%;
  63 + width: 100%;
  64 + }
  65 +
  66 + .device-top {
  67 + padding: 10rpx 30rpx;
  68 + background-color: #fff;
  69 +
  70 + .search {
  71 + display: flex;
  72 + justify-content: space-between;
  73 + padding-bottom: 10rpx;
  74 +
  75 + .search-left {
  76 + width: 580rpx;
  77 + background-color: #f8f9fa;
  78 + border-radius: 200rpx;
  79 + }
  80 +
  81 + .search-right {
  82 + display: flex;
  83 + align-items: center;
  84 +
  85 + text {
  86 + color: #333;
  87 + font-size: 14px;
  88 + }
  89 +
  90 + image {
  91 + width: 40rpx;
  92 + height: 40rpx;
  93 + }
  94 + }
  95 + }
  96 + }
  97 +</style>
\ No newline at end of file
... ...
... ... @@ -228,7 +228,6 @@ export default {
228 228 if (pageLen == 1 && !mainPagePath.includes(currentPages[0].route)) {
229 229 this.firstPage = true;
230 230 this.iconLeft = 'home';
231   - console.log(this.firstPage, 'this.firstPage');
232 231 }
233 232 },
234 233 methods: {
... ... @@ -273,7 +272,6 @@ export default {
273 272 //设置手机状态栏颜色
274 273 settingColor() {
275 274 let navColor = this.navFontColor;
276   - console.log(navColor, 'settingColor');
277 275 let frontColor = '#000000';
278 276 if (whiteList.includes(navColor)) {
279 277 frontColor = '#ffffff';
... ...
... ... @@ -5,14 +5,16 @@ import {
5 5 * 服务端配置项
6 6 * baseUrl 服务端 api地址
7 7 * baseDrawioUrl 组态地址 注意端口
  8 + * baseVisualUrl 看板地址
8 9 * baseWebSocketUrl 服务端 websocket地址
9 10 * socketPrefix websocket前缀 ((https, wss),( http, ws))
10 11 */
11   -
12   -const baseUrl = "https://demo.thingskit.com/api";
13   -const baseDrawioUrl = "https://demo.thingskit.com/thingskit-scada";
14   -const baseWebSocketUrl = "demo.thingskit.com";
15   -const socketPrefix = "wss";
  12 +const baseUrl = "http://222.180.200.114:48080/api";
  13 +const baseVisualUrl = "http://222.180.200.114:9527"
  14 +// const baseVisualUrl = "http://192.168.1.4:8083"
  15 +const baseDrawioUrl = "http://222.180.200.114:9527/thingskit-scada";
  16 +const baseWebSocketUrl = "222.180.200.114:48080";
  17 +const socketPrefix = "ws";
16 18
17 19 let systemInfo = {
18 20 ...getTabbarHeight(),
... ... @@ -42,6 +44,7 @@ systemInfo.platform = "plus";
42 44 const courtConfig = {
43 45 publicAppId: "", //公众号appId
44 46 baseUrl, //服务端地址
  47 + baseVisualUrl,//服务端看板地址
45 48 baseDrawioUrl, //服务端组态地址
46 49 baseWebSocketUrl, //服务端websocket地址
47 50 socketPrefix, //websocket前缀
... ... @@ -51,4 +54,4 @@ const courtConfig = {
51 54 sk: "",
52 55 },
53 56 };
54   -export default Object.assign({}, courtConfig);
  57 +export default Object.assign({}, courtConfig);
... ...
... ... @@ -103,11 +103,11 @@ uni.$u.http.interceptors.response.use(
103 103 const routers = getCurrentPages();
104 104 const currentRoute = routers[routers.length - 1].route;
105 105 const isLoginPage = currentRoute.includes(
106   - "publicLoginSubPage/public/login"
  106 + "login-subpackage/public/login"
107 107 );
108 108 !isLoginPage &&
109 109 uni.reLaunch({
110   - url: "/publicLoginSubPage/public/login",
  110 + url: "/login-subpackage/public/login",
111 111 });
112 112 // 清空登录信息
113 113 store.commit("emptyUserInfo");
... ...
  1 +//上传图片大小
  2 +const UPLOAD_FILE_SIZE = 5242880
  3 +
  4 +export {
  5 + UPLOAD_FILE_SIZE
  6 +}
\ No newline at end of file
... ...
device-subpackage/device-detail/api/index.js renamed from deviceSubPage/deviceDetailPage/api/index.js
1   -// 获取某个Key的历史数据
2   -const getUrlParams = (params) => {
3   - return Object.keys(params).reduce((prev, next, currentIndex) => {
4   - if (params[next]) {
5   - return prev += `${!currentIndex ? '?' : '&'}${next}=${params[next]}`
6   - }
7   - return prev
8   - }, '')
9   -}
10   -
11   -export function getHistoryData(params) {
12   - let {
13   - entityId
14   - } = params
15   - params = getUrlParams(params)
16   - return uni.$u.http.get(
17   - `/plugins/telemetry/DEVICE/${entityId}/values/timeseries${params}`
18   - );
19   -}
20   -
21   -
22   -
23   -// 获取当前设备的key
24   -export function getDeviceKeys(id) {
25   - return uni.$u.http.get(`/plugins/telemetry/DEVICE/${id}/keys/timeseries`);
26   -};
27   -
28   -export function issueCommand(type, tbDeviceId, data) {
29   - return uni.$u.http.post(`/rpc/${type==='OneWay'?'oneway':'twoway'}/${tbDeviceId}`, data)
  1 +// 获取某个Key的历史数据
  2 +const getUrlParams = (params) => {
  3 + return Object.keys(params).reduce((prev, next, currentIndex) => {
  4 + if (params[next]) {
  5 + return prev += `${!currentIndex ? '?' : '&'}${next}=${params[next]}`
  6 + }
  7 + return prev
  8 + }, '')
30 9 }
  10 +
  11 +export function getHistoryData(params) {
  12 + let {
  13 + entityId
  14 + } = params
  15 + params = getUrlParams(params)
  16 + return uni.$u.http.get(
  17 + `/plugins/telemetry/DEVICE/${entityId}/values/timeseries${params}`
  18 + );
  19 +}
  20 +
  21 +
  22 +
  23 +// 获取当前设备的key
  24 +export function getDeviceKeys(id) {
  25 + return uni.$u.http.get(`/plugins/telemetry/DEVICE/${id}/keys/timeseries`);
  26 +};
... ...
  1 +<template>
  2 + <view class="alert-page">
  3 + <!-- 告警头部 -->
  4 + <view class="filter-button" @click="openSearchDialog">
  5 + <text>筛选</text>
  6 + <image src="/static/shaixuan.png" />
  7 + </view>
  8 + <!-- 告警分页 -->
  9 + <mescroll-uni height="700rpx;" ref="mescrollRef" @init="mescrollInit" :up="upOption" :down="downOption"
  10 + @down="downCallback" @up="upCallback">
  11 + <alarm-item :list="list" @openAlertDetail="openAlertDetail"></alarm-item>
  12 + <mescroll-empty v-if="!list.length" />
  13 + </mescroll-uni>
  14 + <view style="height: 20rpx"></view>
  15 + <!-- 告警筛选 -->
  16 + <alarm-popup ref="alarmPopupRef" :show="show" @close="close" @queryCondition="getQueryCondition"></alarm-popup>
  17 + </view>
  18 +</template>
  19 +<script>
  20 + import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
  21 + import api from '@/api/index.js'
  22 + import alarmItem from '@/pages/alarm/components/alarm-item.vue'
  23 + import alarmPopup from '@/pages/alarm/components/alarm-popup.vue'
  24 + import {
  25 + useNavigateTo
  26 + } from '@/plugins/utils.js'
  27 +
  28 + export default {
  29 + mixins: [MescrollMixin],
  30 + components: {
  31 + alarmItem,
  32 + alarmPopup
  33 + },
  34 + props: {
  35 + deviceId: {
  36 + type: String,
  37 + default: ''
  38 + }
  39 + },
  40 + data() {
  41 + return {
  42 + show: false,
  43 + list: [],
  44 + total: 0,
  45 + downOption: {
  46 + auto: true //是否在初始化后,自动执行downCallback; 默认true
  47 + },
  48 + upOption: {
  49 + auto: false // 不自动加载
  50 + },
  51 + page: {
  52 + num: 0,
  53 + size: 10
  54 + },
  55 + conditions: {},
  56 + };
  57 + },
  58 + methods: {
  59 + disabledScroll() {
  60 + return;
  61 + },
  62 + getQueryCondition(value) {
  63 + this.conditions = value
  64 + this.conditions.deviceId = this.deviceId
  65 + this.loadData(1, this.conditions);
  66 + this.close()
  67 + },
  68 + resetQuery() {
  69 + this.page.num = 1;
  70 + this.$nextTick(() => {
  71 + this.$refs.alarmPopupRef.resetQuery()
  72 + })
  73 + this.conditions = {}
  74 + },
  75 + downCallback() {
  76 + this.list = [];
  77 + this.page.num = 1;
  78 + this.resetQuery();
  79 + this.loadData(this.page.num, {
  80 + deviceId: this.deviceId
  81 + });
  82 + },
  83 + upCallback() {
  84 + const deviceId = {
  85 + deviceId: this.deviceId
  86 + }
  87 + const condition = Object.values(this.conditions)
  88 + if (condition.length === 0) {
  89 + this.page.num += 1;
  90 + this.loadData(this.page.num, {
  91 + ...deviceId
  92 + });
  93 + } else if (condition.filter(Boolean).length > 0) {
  94 + this.page.num += 1;
  95 + this.loadData(this.page.num, {
  96 + ...this.conditions,
  97 + ...deviceId
  98 + });
  99 + } else {
  100 + this.page.num += 1;
  101 + this.loadData(this.page.num, {
  102 + ...deviceId
  103 + });
  104 + }
  105 + },
  106 + async loadData(page, param) {
  107 + let that = this;
  108 + let params = {
  109 + page,
  110 + pageSize: 10,
  111 + ...param
  112 + };
  113 + const res = await api.alarmApi.getAlarmApi({
  114 + params,
  115 + custom: {
  116 + load: false
  117 + }
  118 + })
  119 + if (!res) return
  120 + uni.stopPullDownRefresh();
  121 + that.mescroll.endByPage(res.items.length, res.total); //必传参数(当前页的数据个数, 总页数)
  122 + that.alarmTotal = res.total;
  123 + if (page == 1) {
  124 + that.list = res.items;
  125 + } else {
  126 + that.list = that.list.concat(res.items);
  127 + }
  128 + },
  129 + openSearchDialog() {
  130 + this.show = true;
  131 + this.resetQuery();
  132 + },
  133 + close() {
  134 + this.show = false;
  135 + },
  136 + openAlertDetail(e) {
  137 + useNavigateTo('/alarm-subpackage/alarm-detail/alarm-detail?data=', e)
  138 + }
  139 + }
  140 + };
  141 +</script>
  142 +
  143 +<style lang="scss" scoped>
  144 + .filter-button {
  145 + margin: 0 20rpx;
  146 + font-size: 12px;
  147 + width: 160rpx;
  148 + height: 64rpx;
  149 + border-radius: 32rpx;
  150 + display: flex;
  151 + justify-content: center;
  152 + align-items: center;
  153 + background: #f0f1f2;
  154 + color: #666;
  155 +
  156 + image {
  157 + width: 28rpx;
  158 + height: 28rpx;
  159 + margin-left: 4rpx;
  160 + }
  161 + }
  162 +</style>
\ No newline at end of file
... ...
device-subpackage/device-detail/components/basic-info.vue renamed from deviceSubPage/deviceDetailPage/tabDetail/basicInfo.vue
1   -<template>
2   - <view class="basic-page">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module />
5   - <view class="basic-title">
6   - <view class="u-flex">
7   - <view style="padding-left: 32rpx;">
8   - <u-icon v-if="deviceDetail.deviceInfo.longitude !== ''" @click="handleClick" name="map-fill">
9   - </u-icon>
10   - </view>
11   - <view class="text-clip" style="margin-left: 20rpx;width:370rpx">
12   - {{ deviceDetail.alias? deviceDetail.alias: deviceDetail.name }}
13   - </view>
14   - <view style="margin-left: 20rpx; font-size: 14px;"
15   - :style="{ color: deviceDetail.deviceState === 'INACTIVE' ? '#666' : deviceDetail.deviceState === 'ONLINE' ? '#377DFF' : '#DE4437' }">
16   - {{ deviceStatus }}
17   - </view>
18   - </view>
19   - <view style="margin-right: 20rpx;"
20   - v-if="deviceDetail.deviceState === 'ONLINE' && deviceDetail.deviceType !== 'SENSOR'">
21   - <!-- #ifdef MP -->
22   - <u-button type="primary" shape="circle" size="mini" text="下发命令" @click="showModalBtn" />
23   - <!-- #endif -->
24   - <!-- #ifdef APP-PLUS -->
25   - <view class="cu-item" @tap="showModal" data-target="Modal">
26   - <text>下发命令</text>
27   - </view>
28   - <!-- #endif -->
29   - </view>
30   - </view>
31   - <view class="detail">
32   - <view class="detail-item">
33   - <view class="detail-label">设备编号</view>
34   - <view class="detail-value">{{ deviceDetail.sn }}</view>
35   - </view>
36   - <u-line length="90%" margin="0 auto"></u-line>
37   - <view class="detail-item">
38   - <view class="detail-label">设备类型</view>
39   - <view class="detail-value">{{ deviceType }}</view>
40   - </view>
41   - <u-line length="90%" margin="0 auto"></u-line>
42   - <view class="detail-item">
43   - <view class="detail-label">所属组织</view>
44   - <view class="detail-value">{{ deviceDetail.organizationDTO.name }}</view>
45   - </view>
46   - <u-line length="90%" margin="0 auto"></u-line>
47   - <view class="detail-item">
48   - <view class="detail-label">最后连接时间</view>
49   - <view class="detail-value">{{ formatLastOnlineTime }}</view>
50   - </view>
51   - <u-line length="90%" margin="0 auto"></u-line>
52   - <view class="detail-item">
53   - <view class="detail-label">是否告警</view>
54   - <view class="detail-value">{{ alarmStatus }}</view>
55   - </view>
56   - <u-line length="90%" margin="0 auto"></u-line>
57   - <view class="detail-item">
58   - <view class="detail-label">设备描述</view>
59   - <view class="detail-value">{{ deviceDetail.description }}</view>
60   - </view>
61   - </view>
62   - <!-- 下发命令 -->
63   - <!-- #ifdef APP-PLUS -->
64   - <view v-show="showNodal" class="cu-modal" :class="modalName=='Modal'?'show':''">
65   - <view class="cu-dialog" style="">
66   - <view class="modal_main">
67   - <view class='nav-list margin-top'>
68   - <view style="width: 100%; padding: 0 30rpx;">
69   - <view style="text-align: center; font-weight:700;margin-top: 40rpx;">命令下发</view>
70   - <view style="height: 20rpx;"></view>
71   - <view class="u-flex">
72   - <text
73   - style="color: #333; font-size: 14px;font-weight:700;margin-right: 30rpx;">下发类型:</text>
74   - <view>
75   - <radio-group style="display: flex;" @change="radioChange">
76   - <label class="uni-list-cell uni-list-cell-pd" v-for="(item, index) in items"
77   - :key="item.value">
78   - <view style="display: flex">
79   - <view style="margin-left: 10rpx;">
80   - <radio :value="item.value" :checked="index === current" />
81   - </view>
82   - <view style="width:10rpx"></view>
83   - <view style="margin-left: 10rpx;">{{item.name}}</view>
84   - </view>
85   - </label>
86   - </radio-group>
87   - </view>
88   - </view>
89   - <view style="margin-top: 15rpx">
90   - <view class="cusAppplusContent">
91   - <textarea v-model="inputCommandVal" placeholder="请输入下发内容(json格式)" />
92   - </view>
93   - </view>
94   - <view class="button-group">
95   - <view>
96   - <view class="cusAppplusCancelBtn" @click="cancelCommand"><text
97   - style="color: #333333">取消</text>
98   - </view>
99   - </view>
100   - <view>
101   - <view class="cusAppplusConfrimBtn" @click="confirmCommand">
102   - <text style="color:white">确认</text>
103   - </view>
104   - </view>
105   - </view>
106   - <view style="height:30rpx"></view>
107   - </view>
108   - </view>
109   - </view>
110   - </view>
111   - </view>
112   - <!-- #endif -->
113   - <!-- #ifdef MP -->
114   - <u-modal :show="showModel" closeOnClickOverlay :showConfirmButton="false" width="720rpx" @close="hiddenModal"
115   - @touchmove.stop.prevent="disabledScroll">
116   - <view style="width: 100%; padding: 0 30rpx;">
117   - <view style="text-align: center; font-weight:700;margin-bottom: 40rpx;">命令下发</view>
118   - <view class="u-flex">
119   - <text style="color: #333; font-size: 14px;font-weight:700;margin-right: 30rpx;">下发类型:</text>
120   -
121   - <u-radio-group v-model="commandType" placement="row">
122   - <u-radio activeColor="#3388FF" label="单向" name="OneWay"></u-radio>
123   - <view style="margin: 0 20rpx;"></view>
124   - <u-radio activeColor="#3388FF" label="双向" name="TwoWay"></u-radio>
125   - </u-radio-group>
126   - </view>
127   - <view style="margin-top: 28rpx;width: 100%;">
128   - <div class="u-flex u-row-between">
129   - <u--textarea :placeholder="`请输入下发内容${isShowTCP?'(字符串格式)':'(json格式)'}`" v-model="inputCommandVal" />
130   - <u-icon v-if="!isShowTCP" @click="handleCopy(copyTextValue)" name="question-circle" color="#2979ff" size="28"
131   - class="ml-10">
132   - </u-icon>
133   - </div>
134   - </view>
135   -
136   - <view class="button-group">
137   - <view>
138   - <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="取消"
139   - @click="cancelCommand"></u-button>
140   - </view>
141   - <view>
142   - <u-button color="#3388ff" shape="circle" text="确认" @click="confirmCommand"></u-button>
143   - </view>
144   - </view>
145   - </view>
146   - </u-modal>
147   - <!-- #endif -->
148   - </view>
149   -</template>
150   -
151   -<script>
152   - import {
153   - formatToDate
154   - } from '@/plugins/utils.js';
155   - import {
156   - issueCommand
157   - } from '../api/index.js';
158   - export default {
159   - props: {
160   - deviceDetail: {
161   - type: Object,
162   - default: () => ({})
163   - }
164   - },
165   - data() {
166   - return {
167   - copyTextValue: {
168   - "method": "methodThingskit",
169   - "params": {
170   - "pin": 7,
171   - "value": 1
172   - }
173   - },
174   - showNodal: false,
175   - items: [{
176   - value: 'OneWay',
177   - name: '单向',
178   - checked: 'true'
179   - },
180   - {
181   - value: 'TwoWay',
182   - name: '双向'
183   - },
184   - ],
185   - current: 0,
186   - modalName: null,
187   - showModel: false,
188   - commandType: 'OneWay',
189   - commandValue: {},
190   - inputCommandVal: '',
191   - isShowTCP:false,//用于下发命令时判断是否是TCP/UDP
192   - };
193   - },
194   - computed: {
195   - deviceStatus(){
196   - return this.deviceDetail.deviceState === 'INACTIVE' ? '待激活' : this.deviceDetail.deviceState === 'ONLINE' ? '在线' : '离线'
197   - },
198   - deviceType() {
199   - return this.deviceDetail.deviceType === 'DIRECT_CONNECTION' ?
200   - '直连设备' :
201   - this.deviceDetail.deviceType === 'GATEWAY' ?
202   - '网关设备' :
203   - this.deviceDetail.deviceType === 'SENSOR' ?
204   - '网关子设备' :
205   - '';
206   - },
207   - alarmStatus() {
208   - return this.deviceDetail.alarmStatus === '0' ? '否' : '是';
209   - },
210   - formatLastOnlineTime() {
211   - return formatToDate(Number(this.deviceDetail.lastOnlineTime), 'YYYY-MM-DD HH:mm:ss');
212   - }
213   - },
214   - onLoad(e) {
215   - // 隐藏原生的tabbar
216   - uni.hideTabBar();
217   - },
218   - mounted() {},
219   - beforeCreate() {
220   - this.modalName = null
221   - },
222   - methods: {
223   - handleCopy(value) {
224   - uni.showModal({
225   - content: JSON.stringify(value),
226   - confirmText: '复制内容',
227   - showCancel:false,
228   - success: () => {
229   - uni.setClipboardData({
230   - data: JSON.stringify(value),
231   - success: () => {
232   - uni.showToast({
233   - title: '复制成功'
234   - })
235   - }
236   - });
237   - },
238   -
239   - });
240   - },
241   - radioChange: function(evt) {
242   - for (let i = 0; i < this.items.length; i++) {
243   - if (this.items[i].value === evt.detail.value) {
244   - this.current = i;
245   - break;
246   - }
247   - }
248   - this.commandType = evt.detail.value
249   - },
250   - handleClick() {
251   - const data = {
252   - longitude: this.deviceDetail.deviceInfo.longitude || 0,
253   - latitude: this.deviceDetail.deviceInfo.latitude || 0
254   - };
255   - uni.navigateTo({
256   - url: '/deviceSubPage/deviceDetailPage/devicePosition?data=' + JSON.stringify(data)
257   - });
258   - },
259   - showModal(e) {
260   - this.modalName = e.currentTarget.dataset.target
261   - const {transportType} = this.deviceDetail.deviceProfile
262   - this.isShowTCP = transportType=='TCP'?true:false
263   - this.showNodal = true
264   - },
265   - showModalBtn() {
266   - this.showModel = true;
267   - this.inputCommandVal = '';
268   - const {transportType} = this.deviceDetail.deviceProfile
269   - this.isShowTCP = transportType=='TCP'?true:false
270   - },
271   - disabledScroll() {
272   - return;
273   - },
274   - hiddenModal() {
275   - this.showModel = false;
276   - this.inputCommandVal = '';
277   - // #ifdef APP-PLUS
278   - this.modalName = null
279   - this.showNodal = false
280   - // #endif
281   - },
282   - async confirmCommand() {
283   - try {
284   - this.commandValue.method = 'methodThingskit';
285   - this.commandValue.persistent = true;
286   - this.commandValue.additionalInfo = {
287   - cmdType: 'API'
288   - };
289   - if(this.isShowTCP){//TCP的格式只能是字符串
290   - const zg = /^[0-9a-zA-Z]*$/
291   - if(!zg.test(this.inputCommandVal)) {
292   - uni.$u.toast('输入的内容只能是字母和数字的组合')
293   - return
294   - }
295   - this.commandValue.params = this.inputCommandVal
296   - }else{
297   - const commandJsonValue = JSON.parse(this.inputCommandVal);
298   - this.commandValue.params = commandJsonValue
299   - }
300   - await issueCommand(this.commandType, this.deviceDetail.tbDeviceId, this.commandValue);
301   - this.hiddenModal();
302   - uni.$u.toast('下发成功~');
303   - } catch (e) {
304   - uni.$u.toast('下发失败~');
305   - }
306   - },
307   - cancelCommand() {
308   - this.hiddenModal();
309   - // #ifdef APP-PLUS
310   - this.modalName = null
311   - this.showNodal = false
312   - // #endif
313   - }
314   - }
315   - };
316   -</script>
317   -
318   -<style lang="scss" scoped>
319   - @import url('../styles/modal.css');
320   -
321   - .cusAppplusContent {
322   - width: 625rpx;
323   - height: 400rpx;
324   - background: #FFFFFF;
325   - box-shadow: 2px 2px 4px 0px rgba(0, 0, 0, 0.03);
326   - border-radius: 10px;
327   -
328   - }
329   -
330   - .cusAppplusCancelBtn {
331   - background: #e3e3e5;
332   - border-radius: 38rpx;
333   - height: 85rpx;
334   - line-height: 85rpx
335   - }
336   -
337   - .cusAppplusConfrimBtn {
338   - background: #3388ff;
339   - border-radius: 38rpx;
340   - height: 85rpx;
341   - line-height: 85rpx
342   - }
343   -
344   - .cu-item {
345   - background: #3388FF;
346   - border-radius: 12px;
347   - width: 120rpx;
348   - height: 48rpx;
349   - text-align: center;
350   - line-height: 40rpx;
351   -
352   - text {
353   - font-size: 12px;
354   - font-family: PingFangSC-Regular, PingFang SC;
355   - font-weight: 400;
356   - color: #FFFFFF;
357   - }
358   - }
359   -
360   -
361   -
362   - .basic-page {
363   - padding: 0 30rpx;
364   -
365   - .basic-title {
366   - display: flex;
367   - justify-content: space-between;
368   - align-items: center;
369   - height: 140rpx;
370   - background-color: #fff;
371   - border-radius: 20rpx;
372   - }
373   -
374   - .detail {
375   - background-color: #fff;
376   - margin-top: 30rpx;
377   - border-radius: 20rpx;
378   - width: 690rpx;
379   -
380   - .detail-item {
381   - padding: 30rpx;
382   - display: flex;
383   - align-items: center;
384   -
385   - .detail-label {
386   - color: #333;
387   - font-size: 15px;
388   - }
389   -
390   - .detail-value {
391   - color: #666;
392   - font-size: 14px;
393   - margin-left: 30rpx;
394   - }
395   - }
396   - }
397   - }
398   -
399   - /deep/ .u-modal__content {
400   - padding: 30rpx 0 !important;
401   - }
402   -
403   - .button-group {
404   - display: flex;
405   - margin-top: 40rpx;
406   - justify-content: space-between;
407   -
408   - view {
409   - width: 300rpx;
410   - }
411   - }
412   -</style>
  1 +<template>
  2 + <view class="basic-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <!-- 基础信息头部 -->
  6 + <view class="basic-header">
  7 + <view class="u-flex">
  8 + <view class="pl-3">
  9 + <u-icon v-if="deviceDetail.deviceInfo.longitude !== ''" @click="handleClick"
  10 + name="map-fill"></u-icon>
  11 + </view>
  12 + <view class="basic-text text-clip ml-2">
  13 + {{ deviceDetail.alias ? deviceDetail.alias : deviceDetail.name }}
  14 + </view>
  15 + <view class="basic-text-status ml-2" :style="{ color: formatTextStatus(deviceDetail.deviceState) }">
  16 + {{ deviceStatus }}
  17 + </view>
  18 + </view>
  19 + <!-- 命令下发 设备在线并且不是网关子设备 -->
  20 + <view class="mr-2" v-if="deviceDetail.deviceState === 'ONLINE' && deviceDetail.deviceType !== 'SENSOR'">
  21 + <!-- #ifdef MP -->
  22 + <u-button type="primary" shape="circle" size="mini" text="下发命令" @click="handleMpShowModal" />
  23 + <!-- #endif -->
  24 + <!-- #ifdef APP-PLUS -->
  25 + <view class="cu-item" @tap="handleAppShowModal" data-target="Modal">
  26 + <text>下发命令</text>
  27 + </view>
  28 + <!-- #endif -->
  29 + </view>
  30 + </view>
  31 + <!-- 设备详情 -->
  32 + <view class="detail">
  33 + <view class="detail-item">
  34 + <view class="detail-label">设备编号</view>
  35 + <view class="detail-value">{{ deviceDetail.sn }}</view>
  36 + </view>
  37 + <u-line length="90%" margin="0 auto"></u-line>
  38 + <view class="detail-item">
  39 + <view class="detail-label">设备类型</view>
  40 + <view class="detail-value">{{ deviceType }}</view>
  41 + </view>
  42 + <u-line length="90%" margin="0 auto"></u-line>
  43 + <view class="detail-item">
  44 + <view class="detail-label">所属组织</view>
  45 + <view class="detail-value">{{ deviceDetail.organizationDTO.name }}</view>
  46 + </view>
  47 + <u-line length="90%" margin="0 auto"></u-line>
  48 + <view class="detail-item">
  49 + <view class="detail-label">最后连接时间</view>
  50 + <view class="detail-value">{{ formatLastOnlineTime }}</view>
  51 + </view>
  52 + <u-line length="90%" margin="0 auto"></u-line>
  53 + <view class="detail-item">
  54 + <view class="detail-label">是否告警</view>
  55 + <view class="detail-value">{{ alarmStatus }}</view>
  56 + </view>
  57 + <u-line length="90%" margin="0 auto"></u-line>
  58 + <view class="detail-item">
  59 + <view class="detail-label">设备描述</view>
  60 + <view class="detail-value">{{ deviceDetail.description }}</view>
  61 + </view>
  62 + </view>
  63 + <!-- 命令下发 -->
  64 + <!-- #ifdef APP-PLUS -->
  65 + <!-- 原生弹窗 封装成子组件无效 -->
  66 + <view v-show="showNativeModal" class="cu-modal" :class="modalName == 'Modal' ? 'show' : ''">
  67 + <view class="cu-dialog">
  68 + <view class="app-command-content">
  69 + <view class="app-command-text">
  70 + <text>命令下发</text>
  71 + </view>
  72 + <view class="app-command-type">
  73 + <text>下发类型</text>
  74 + <view class="mr-2">
  75 + <radio-group @change="radioChange" class="flex mr-1">
  76 + <label v-for="(item, index) in commandTypeList" :key="item.value">
  77 + <view class="flex">
  78 + <view class="ml-1">
  79 + <radio :value="item.value" :checked="index === current" />
  80 + </view>
  81 + <view style="width:10rpx"></view>
  82 + <view class="ml-1">{{item.name}}</view>
  83 + </view>
  84 + </label>
  85 + </radio-group>
  86 + </view>
  87 + </view>
  88 + <view class="app-command-body">
  89 + <textarea class="app-command-textarea" v-model="inputCommandContent"
  90 + :placeholder="`请输入下发内容${isShowTCP?'(字符串格式)':'(json格式)'}`" />
  91 + <u-icon @click="handleCopy(copyTextValue)" v-if="!isShowTCP" name="question-circle"
  92 + color="#2979ff" size="28" class="ml-10">
  93 + </u-icon>
  94 + </view>
  95 + <view class="app-command-buttons">
  96 + <view class="cancel-button" @click="cancelCommand"><text class="cancel-text">取消</text></view>
  97 + <view @click="handleAppCommand" class="confrim-button"><text class="confrim-text">确认</text>
  98 + </view>
  99 + </view>
  100 + </view>
  101 + </view>
  102 + </view>
  103 + <!-- #endif -->
  104 + <!-- #ifdef MP -->
  105 + <!-- u-modal在app端弹窗层级无法覆盖背景色 -->
  106 + <mp-command-issuance ref="mpCommandIssuanceRef" :isShowTCP="isShowTCP" :showModal="mpShowModal"
  107 + @hideModal="hideMpModal" @cancelCommand="cancelCommand"
  108 + @confirmCommand="confirmCommand"></mp-command-issuance>
  109 + <!-- #endif -->
  110 + </view>
  111 +</template>
  112 +
  113 +<script>
  114 + import {formatToDate} from '@/plugins/utils.js';
  115 + import {issueCommand} from '../api/index.js';
  116 + import api from '@/api/index.js';
  117 + import mpCommandIssuance from './mp-command-issuance.vue';
  118 + import {commandTypeList} from '../config/data.js'
  119 + import {useShowModal} from '@/plugins/utils.js'
  120 +
  121 + export default {
  122 + components: {
  123 + mpCommandIssuance,
  124 + },
  125 + props: {
  126 + deviceDetail: {
  127 + type: Object,
  128 + default: () => ({})
  129 + }
  130 + },
  131 + data() {
  132 + return {
  133 + showNativeModal: false,
  134 + current: 0,
  135 + commandTypeList,
  136 + commandTypeStr: 'OneWay',
  137 + inputCommandContent: '',
  138 + mpShowModal: false,
  139 + commandValue: {},
  140 + isShowTCP: false, //用于下发命令时判断是否是TCP/UDP
  141 + modalName: null,
  142 + copyTextValue: {
  143 + "method": "methodThingskit",
  144 + "params": {
  145 + "pin": 7,
  146 + "value": 1
  147 + }
  148 + }
  149 + };
  150 + },
  151 + computed: {
  152 + deviceStatus() {
  153 + return this.deviceDetail.deviceState === 'INACTIVE' ? '待激活' : this.deviceDetail.deviceState === 'ONLINE' ?
  154 + '在线' : '离线';
  155 + },
  156 + deviceType() {
  157 + return this.deviceDetail.deviceType === 'DIRECT_CONNECTION' ?
  158 + '直连设备' :
  159 + this.deviceDetail.deviceType === 'GATEWAY' ?
  160 + '网关设备' :
  161 + this.deviceDetail.deviceType === 'SENSOR' ?
  162 + '网关子设备' :
  163 + '';
  164 + },
  165 + alarmStatus() {
  166 + return this.deviceDetail.alarmStatus === '0' ? '否' : '是';
  167 + },
  168 + formatLastOnlineTime() {
  169 + return formatToDate(Number(this.deviceDetail.lastOnlineTime), 'YYYY-MM-DD HH:mm:ss');
  170 + }
  171 + },
  172 + beforeCreate() {
  173 + this.modalName = null
  174 + },
  175 + onLoad() {
  176 + // 隐藏原生的tabbar
  177 + uni.hideTabBar();
  178 + this.modalName = null
  179 + },
  180 + methods: {
  181 + handleCopy(value) {
  182 + useShowModal(JSON.stringify(value), '命令下发', '复制内容').then(res => {
  183 + uni.setClipboardData({
  184 + data: JSON.stringify(value),
  185 + success: () => {
  186 + uni.showToast({
  187 + title: '复制成功'
  188 + })
  189 + }
  190 + });
  191 + })
  192 + },
  193 + radioChange: function(evt) {
  194 + for (let i = 0; i < this.commandTypeList.length; i++) {
  195 + if (this.items[i].value === evt.detail.value) {
  196 + this.current = i;
  197 + break;
  198 + }
  199 + }
  200 + this.commandTypeStr = evt.detail.value
  201 + },
  202 + formatTextStatus(deviceState) {
  203 + return deviceState === 'INACTIVE' ? '#666' : deviceState === 'ONLINE' ? '#377DFF' : '#DE4437';
  204 + },
  205 + handleClick() {
  206 + const data = {
  207 + longitude: this.deviceDetail.deviceInfo.longitude || 0,
  208 + latitude: this.deviceDetail.deviceInfo.latitude || 0
  209 + };
  210 + uni.navigateTo({
  211 + url: '/device-subpackage/device-detail/device-position?data=' + JSON.stringify(data)
  212 + });
  213 + },
  214 + disabledScroll() {
  215 + return;
  216 + },
  217 + handleAppShowModal(e) {
  218 + this.modalName = e.currentTarget.dataset.target;
  219 + this.showNativeModal = true
  220 + },
  221 + handleMpShowModal() {
  222 + const {
  223 + transportType
  224 + } = this.deviceDetail.deviceProfile;
  225 + this.isShowTCP = transportType == 'TCP' ? true : false;
  226 + this.mpShowModal = true;
  227 + },
  228 + hideMpModal() {
  229 + this.mpShowModal = false;
  230 + },
  231 + hideAppModal() {
  232 + this.modalName = null;
  233 + this.showNativeModal = false
  234 + },
  235 + confirmCommand(commandType, inputCommandVal) {
  236 + this.handleCommand(commandType, inputCommandVal)
  237 + },
  238 + cancelCommand() {
  239 + this.hideMpModal();
  240 + this.hideAppModal();
  241 + this.commandTypeStr = 'OneWay'
  242 + this.inputCommandContent = ''
  243 + this.$nextTick(() => {
  244 + this.$refs.mpCommandIssuanceRef.reset()
  245 + })
  246 + },
  247 + handleAppCommand() {
  248 + this.handleCommand(this.commandTypeStr, this.inputCommandContent)
  249 + },
  250 + async handleCommand(commandType, inputCommandVal) {
  251 + if (!inputCommandVal) return uni.$u.toast('请输入下发内容~');
  252 + if (this.isShowTCP) {
  253 + //TCP的格式只能是字符串
  254 + const zg = /^[0-9a-zA-Z]*$/;
  255 + if (!zg.test(inputCommandVal)) {
  256 + uni.$u.toast('输入的内容只能是字母和数字的组合');
  257 + return;
  258 + }
  259 + this.commandValue.params = inputCommandVal;
  260 + } else {
  261 + this.commandValue.params = JSON.parse(inputCommandVal);
  262 + }
  263 + this.commandValue.persistent = true;
  264 + this.commandValue.additionalInfo = {
  265 + cmdType: 'API'
  266 + };
  267 + this.commandValue.method = 'methodThingskit';
  268 + this.commandValue.params = inputCommandVal;
  269 + await api.deviceApi.issueCommand(commandType, this.deviceDetail.tbDeviceId, this.commandValue);
  270 + this.cancelCommand();
  271 + uni.$u.toast('下发成功~');
  272 +
  273 + },
  274 +
  275 + }
  276 + };
  277 +</script>
  278 +
  279 +<style lang="scss" scoped>
  280 + @import url('../static/modal.css');
  281 +
  282 + .app-command-content {
  283 + .app-command-text {
  284 + display: flex;
  285 + justify-content: center;
  286 + align-items: center;
  287 +
  288 + text {
  289 + font-weight: 700;
  290 + }
  291 + }
  292 +
  293 + .app-command-type {
  294 + display: flex;
  295 + margin-top: 20rpx;
  296 + margin-left: 20rpx;
  297 +
  298 + text {
  299 + font-weight: 700;
  300 + }
  301 + }
  302 +
  303 + .app-command-body {
  304 + display: flex;
  305 + align-items: center;
  306 + justify-content: space-between;
  307 + margin-top: 20rpx;
  308 + margin-left: 20rpx;
  309 +
  310 + .app-command-textarea {
  311 + width: 625rpx;
  312 + height: 400rpx;
  313 + background: #FFFFFF;
  314 + box-shadow: 2px 2px 4px 0px rgba(0, 0, 0, 0.03);
  315 + border-radius: 10px;
  316 + }
  317 + }
  318 +
  319 + .app-command-buttons {
  320 + display: flex;
  321 + align-items: center;
  322 + justify-content: space-evenly;
  323 + height: 85rpx;
  324 + margin-top: 20rpx;
  325 + margin-bottom: 20rpx;
  326 +
  327 + .cancel-button {
  328 + width: 300rpx;
  329 + background: #e3e3e5;
  330 + height: 85rpx;
  331 + border-radius: 38rpx;
  332 + line-height: 85rpx;
  333 +
  334 + .cancel-text {
  335 + color: #333333
  336 + }
  337 + }
  338 +
  339 + .confrim-button {
  340 + width: 300rpx;
  341 + background: #3388ff;
  342 + border-radius: 38rpx;
  343 + height: 85rpx;
  344 + line-height: 85rpx;
  345 +
  346 + .confrim-text {
  347 + color: white
  348 + }
  349 + }
  350 + }
  351 + }
  352 +
  353 + .basic-page {
  354 + padding: 0 30rpx;
  355 +
  356 + .basic-header {
  357 + display: flex;
  358 + justify-content: space-between;
  359 + align-items: center;
  360 + height: 140rpx;
  361 + background-color: #fff;
  362 + border-radius: 20rpx;
  363 +
  364 + .basic-text {
  365 + width: 370rpx;
  366 + }
  367 +
  368 + .cu-item {
  369 + background: #3388ff;
  370 + border-radius: 12px;
  371 + width: 120rpx;
  372 + height: 48rpx;
  373 + text-align: center;
  374 + line-height: 40rpx;
  375 +
  376 + text {
  377 + font-size: 12px;
  378 + font-family: PingFangSC-Regular, PingFang SC;
  379 + font-weight: 400;
  380 + color: #ffffff;
  381 + }
  382 + }
  383 +
  384 + .basic-text-status {
  385 + font-size: 14px;
  386 + }
  387 + }
  388 +
  389 + .detail {
  390 + background-color: #fff;
  391 + margin-top: 30rpx;
  392 + border-radius: 20rpx;
  393 + width: 690rpx;
  394 +
  395 + .detail-item {
  396 + padding: 30rpx;
  397 + display: flex;
  398 + align-items: center;
  399 +
  400 + .detail-label {
  401 + color: #333;
  402 + font-size: 15px;
  403 + }
  404 +
  405 + .detail-value {
  406 + color: #666;
  407 + font-size: 14px;
  408 + margin-left: 30rpx;
  409 + }
  410 + }
  411 + }
  412 + }
  413 +
  414 + /deep/ .u-modal__content {
  415 + padding: 30rpx 0 !important;
  416 + }
  417 +</style>
\ No newline at end of file
... ...
device-subpackage/device-detail/components/command-detail.vue renamed from deviceSubPage/deviceDetailPage/tabDetail/CommandDetail.vue
1   -<template>
2   - <view class="command-detail">
3   - <view class="detail-top">{{ commandDetail.deviceName }}</view>
4   - <view class="detail">
5   - <view class="detail-item">
6   - <view class="detail-label">设备类型</view>
7   - <view class="detail-value">{{ deviceType }}</view>
8   - </view>
9   - <u-line length="90%" margin="0 auto"></u-line>
10   - <view class="detail-item">
11   - <view class="detail-label">设备编号</view>
12   - <view class="detail-value">{{ commandDetail.deviceSn }}</view>
13   - </view>
14   - <u-line length="90%" margin="0 auto"></u-line>
15   - <view class="detail-item">
16   - <view class="detail-label">所属组织</view>
17   - <view class="detail-value">{{ commandDetail.organizationName }}</view>
18   - </view>
19   - <u-line length="90%" margin="0 auto"></u-line>
20   - <view class="detail-item">
21   - <view class="detail-label">命令下发时间</view>
22   - <view class="detail-value">{{ format(commandDetail.createTime) }}</view>
23   - </view>
24   - <u-line length="90%" margin="0 auto"></u-line>
25   - <view class="detail-item">
26   - <view class="detail-label">命令类型</view>
27   - <view class="detail-value">{{ commandDetail.additionalInfo.cmdType===1?'服务':'自定义' }}</view>
28   - </view>
29   - <u-line length="90%" margin="0 auto" v-if="commandDetail.additionalInfo.cmdType"></u-line>
30   - <view class="detail-item">
31   - <view class="detail-label">响应类型</view>
32   - <view class="detail-value">{{ commandDetail.request.oneway ? '单向' : '双向' }}</view>
33   - </view>
34   - <u-line length="90%" margin="0 auto"></u-line>
35   - <view class="detail-item">
36   - <view class="detail-label">命令状态</view>
37   - <view class="detail-value">{{ commandDetail.statusName }}</view>
38   - </view>
39   - <u-line length="90%" margin="0 auto"></u-line>
40   - <view class="detail-item" v-if="!commandDetail.request.oneway">
41   - <view class="detail-label">响应结果</view>
42   - <view class="detail-value">{{ commandDetail.response?JSON.stringify(commandDetail.response):'无' }}</view>
43   - </view>
44   - <!-- <view class="detail-item" v-if="!commandDetail.request.oneway">
45   - <view class="detail-label">响应失败内容</view>
46   - <view class="detail-value" style="width: 400rpx;" v-if="commandDetail.response.status!=='SUCCESS'">
47   - <u--textarea placeholder="响应失败内容" v-model="failContent" />
48   - </view>
49   - </view> -->
50   - </view>
51   - <view class="command">命令内容</view>
52   - <u-textarea :value="formatValue(commandDetail.request.body)" disabled></u-textarea>
53   - <view style="height: 50rpx;"></view>
54   - </view>
55   -</template>
56   -
57   -<script>
58   - import {
59   - formatToDate
60   - } from '@/plugins/utils.js';
61   - export default {
62   - data() {
63   - return {
64   - commandDetail: {},
65   - failContent: ""
66   - };
67   - },
68   - computed: {
69   - deviceType() {
70   - return this.commandDetail.deviceType === 'DIRECT_CONNECTION' ?
71   - '直连设备' :
72   - this.commandDetail.deviceType === 'GATEWAY' ?
73   - '网关设备' :
74   - this.commandDetail.deviceType === 'SENSOR' ?
75   - '网关子设备' :
76   - '';
77   - }
78   - },
79   - methods: {
80   - format(date) {
81   - return formatToDate(date, 'YYYY-MM-DD HH:mm:ss');
82   - },
83   - formatValue(value) {
84   - try {
85   - const val = JSON.parse(value['params']);
86   - //微信小程序端object无法显示,格式化为字符串
87   - const stringifyVal = JSON.stringify(val['params'])
88   - const formatVal = stringifyVal
89   - .replace(/\\"/g, '"')
90   - .replace(/]"/g, ']')
91   - .replace(/"\[/g, '[');
92   - return formatVal
93   - } catch (e) {
94   - console.error("命令记录页面格式化无返回值", e);
95   - return value['params']
96   - }
97   - }
98   - },
99   - onLoad(options) {
100   - const {
101   - data
102   - } = options;
103   - this.commandDetail = JSON.parse(data);
104   - if (this.commandDetail.response.status === 'SUCCESS') return
105   - this.failContent = JSON.stringify(this.commandDetail.response.error)
106   - }
107   - };
108   -</script>
109   -
110   -<style lang="scss" scoped>
111   - .command-detail {
112   - padding: 0 30rpx;
113   - height: 100vh;
114   - background-color: #f8f9fa;
115   -
116   - .detail-top {
117   - height: 118rpx;
118   - width: 690rpx;
119   - display: flex;
120   - align-items: center;
121   - background-color: #fff;
122   - color: #333;
123   - border-radius: 20rpx;
124   - font-size: 15px;
125   - margin-top: 30rpx;
126   - padding: 30rpx;
127   - }
128   -
129   - .detail {
130   - background-color: #fff;
131   - margin-top: 30rpx;
132   - border-radius: 20rpx;
133   - width: 690rpx;
134   -
135   - .detail-item {
136   - padding: 30rpx;
137   - display: flex;
138   - align-items: center;
139   -
140   - .detail-label {
141   - color: #333;
142   - font-size: 15px;
143   - }
144   -
145   - .detail-value {
146   - color: #666;
147   - font-size: 14px;
148   - margin-left: 30rpx;
149   - }
150   - }
151   - }
152   -
153   - .command {
154   - margin: 30rpx 0;
155   - }
156   - }
157   -</style>
  1 +<template>
  2 + <view class="command-detail">
  3 + <view class="detail-top">{{ commandDetail.deviceName }}</view>
  4 + <view class="detail">
  5 + <view class="detail-item">
  6 + <view class="detail-label">设备类型</view>
  7 + <view class="detail-value">{{ deviceType }}</view>
  8 + </view>
  9 + <u-line length="90%" margin="0 auto"></u-line>
  10 + <view class="detail-item">
  11 + <view class="detail-label">设备编号</view>
  12 + <view class="detail-value">{{ commandDetail.deviceSn }}</view>
  13 + </view>
  14 + <u-line length="90%" margin="0 auto"></u-line>
  15 + <view class="detail-item">
  16 + <view class="detail-label">所属组织</view>
  17 + <view class="detail-value">{{ commandDetail.organizationName }}</view>
  18 + </view>
  19 + <u-line length="90%" margin="0 auto"></u-line>
  20 + <view class="detail-item">
  21 + <view class="detail-label">命令下发时间</view>
  22 + <view class="detail-value">{{ format(commandDetail.createTime) }}</view>
  23 + </view>
  24 + <u-line length="90%" margin="0 auto"></u-line>
  25 + <view class="detail-item">
  26 + <view class="detail-label">命令类型</view>
  27 + <view class="detail-value">{{ commandDetail.additionalInfo.cmdType===1?'服务':'自定义' }}</view>
  28 + </view>
  29 + <u-line length="90%" margin="0 auto" v-if="commandDetail.additionalInfo.cmdType"></u-line>
  30 + <view class="detail-item">
  31 + <view class="detail-label">响应类型</view>
  32 + <view class="detail-value">{{ commandDetail.request.oneway ? '单向' : '双向' }}</view>
  33 + </view>
  34 + <u-line length="90%" margin="0 auto"></u-line>
  35 + <view class="detail-item">
  36 + <view class="detail-label">命令状态</view>
  37 + <view class="detail-value">{{ commandDetail.statusName }}</view>
  38 + </view>
  39 + <u-line length="90%" margin="0 auto"></u-line>
  40 + <view class="detail-item" v-if="!commandDetail.request.oneway">
  41 + <view class="detail-label">响应结果</view>
  42 + <view class="detail-value">{{ commandDetail.response?JSON.stringify(commandDetail.response):'无' }}
  43 + </view>
  44 + </view>
  45 + </view>
  46 + <view class="command">命令内容</view>
  47 + <u-textarea :value="formatValue(commandDetail.request.body)" disabled></u-textarea>
  48 + <view style="height: 50rpx;"></view>
  49 + </view>
  50 +</template>
  51 +
  52 +<script>
  53 + import {
  54 + formatToDate
  55 + } from '@/plugins/utils.js';
  56 + export default {
  57 + data() {
  58 + return {
  59 + commandDetail: {},
  60 + failContent: ""
  61 + };
  62 + },
  63 + computed: {
  64 + deviceType() {
  65 + return this.commandDetail.deviceType === 'DIRECT_CONNECTION' ?
  66 + '直连设备' :
  67 + this.commandDetail.deviceType === 'GATEWAY' ?
  68 + '网关设备' :
  69 + this.commandDetail.deviceType === 'SENSOR' ?
  70 + '网关子设备' :
  71 + '';
  72 + }
  73 + },
  74 + methods: {
  75 + format(date) {
  76 + return formatToDate(date, 'YYYY-MM-DD HH:mm:ss');
  77 + },
  78 + formatValue(value) {
  79 + try {
  80 + const val = JSON.parse(value['params']);
  81 + //微信小程序端object无法显示,格式化为字符串
  82 + const stringifyVal = JSON.stringify(val['params'])
  83 + const formatVal = stringifyVal
  84 + .replace(/\\"/g, '"')
  85 + .replace(/]"/g, ']')
  86 + .replace(/"\[/g, '[');
  87 + return formatVal
  88 + } catch (e) {
  89 + return value['params']
  90 + }
  91 + }
  92 + },
  93 + onLoad(options) {
  94 + const { data } = options;
  95 + this.commandDetail = JSON.parse(decodeURIComponent(data));
  96 + if (this.commandDetail.response.status === 'SUCCESS') return
  97 + this.failContent = JSON.stringify(this.commandDetail.response.error)
  98 + }
  99 + };
  100 +</script>
  101 +
  102 +<style lang="scss" scoped>
  103 + @import "../static/command-detail.scss";
  104 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <view class="command-record">
  3 + <!-- 命令记录筛选-->
  4 + <view class="filter-button" @click="openSearchDialog">
  5 + <text>筛选</text>
  6 + <image src="/static/shaixuan.png" />
  7 + </view>
  8 + <!-- 命令记录分页 -->
  9 + <mescroll-uni ref="mescrollRef" @init="mescrollInit" :down="downOption" @down="downCallback" @up="upCallback"
  10 + height="700px">
  11 + <view @click="openCommandDetail(item)" class="list-item" v-for="(item, index) in list" :key="index">
  12 + <view class="item">
  13 + <view class="item-first">
  14 + <text>{{ item.deviceName }}</text>
  15 + <!-- 业务 单向是没有响应状态 -->
  16 + <view v-if="!item.request.oneway">
  17 + <view class="item-right item-success" v-if="item.response">响应成功</view>
  18 + <view class="item-right item-fail" v-else>响应失败</view>
  19 + </view>
  20 + </view>
  21 + <view>
  22 + 命令类型:
  23 + <text class="ml-16">{{ item.additionalInfo.cmdType===1?'服务':'自定义' }}</text>
  24 + </view>
  25 + <view v-if="item.statusName">
  26 + 命令状态:
  27 + <text :style="{color:formatCommandStatus(item.status)}" class="ml-16">
  28 + {{ item.statusName }}
  29 + </text>
  30 + </view>
  31 + <view class="item-first">
  32 + <view>
  33 + 响应类型:
  34 + <text class="ml-16">{{ !item.request.oneway?'双向':'单向' }}</text>
  35 + </view>
  36 + <view class="time">{{ format(item.createTime) }}</view>
  37 + </view>
  38 + </view>
  39 + </view>
  40 + </mescroll-uni>
  41 + <!-- 命令记录弹窗筛选 -->
  42 + <u-popup @close="close" closeable bgColor="#fff" :show="show" mode="bottom" :round="20">
  43 + <view class="filter" @touchmove.stop.prevent="disabledScroll">
  44 + <view class="filter-title"><text>筛选条件</text></view>
  45 + <query-item :filterList="issueStatus" title="下发状态"
  46 + @clickTag="currentIndex => handleClickTag(currentIndex, issueStatus)"></query-item>
  47 + <view class="flex-column">
  48 + <view class="mt-3 command-time-text">命令下发时间</view>
  49 + <view class="mt-3">
  50 + <uni-datetime-picker return-type="timestamp" v-model="range" type="datetimerange"
  51 + rangeSeparator="至" />
  52 + </view>
  53 + </view>
  54 + <view class="h-30"></view>
  55 + <view class="button-group">
  56 + <view>
  57 + <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="重置"
  58 + @click="resetFilter"></u-button>
  59 + </view>
  60 + <view>
  61 + <u-button color="#3388ff" shape="circle" text="确认" @click="confirmFilter"></u-button>
  62 + </view>
  63 + </view>
  64 + </view>
  65 + </u-popup>
  66 + </view>
  67 +</template>
  68 +<script>
  69 + import queryItem from '@/pages/device/components/query-item.vue';
  70 + import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
  71 + import {
  72 + formatToDate
  73 + } from '@/plugins/utils.js';
  74 + import {
  75 + debounce
  76 + } from '@/plugins/throttle.js';
  77 + import {
  78 + issueStatus
  79 + } from '../config/data.js'
  80 + import api from '@/api/index.js'
  81 + import {
  82 + useNavigateTo
  83 + } from '@/plugins/utils.js'
  84 +
  85 + export default {
  86 + mixins: [MescrollMixin],
  87 + components: {
  88 + queryItem
  89 + },
  90 + props: {
  91 + tbDeviceId: {
  92 + type: String,
  93 + default: ''
  94 + }
  95 + },
  96 + data() {
  97 + return {
  98 + show: false,
  99 + list: [],
  100 + total: 0,
  101 + range: [],
  102 + formTime: {
  103 + status: '',
  104 + startTime: '',
  105 + endTime: ''
  106 + },
  107 + status: '',
  108 + issueStatus,
  109 + downOption: {
  110 + auto: false //是否在初始化后,自动执行downCallback; 默认true
  111 + },
  112 + page: {
  113 + num: 0,
  114 + size: 10
  115 + }
  116 + };
  117 + },
  118 + methods: {
  119 + formatCommandStatus(status) {
  120 + return status == 'EXPIRED' ?
  121 + 'red' :
  122 + status == 'DELIVERED' ?
  123 + 'blue' :
  124 + status == 'QUEUED' ?
  125 + '#00C9A7' :
  126 + status == 'TIMEOUT' ?
  127 + 'red' :
  128 + status == 'SENT' ?
  129 + '#00C9A7' : ''
  130 + },
  131 + downCallback() {
  132 + for (let i in this.formTime) Reflect.set(this.formTime, i, '')
  133 + this.list = [];
  134 + this.page.num = 1;
  135 + this.loadData(this.page.num, {
  136 + tbDeviceId: this.tbDeviceId
  137 + });
  138 + },
  139 + format(date) {
  140 + return formatToDate(date, 'YYYY-MM-DD HH:mm:ss');
  141 + },
  142 + disabledScroll() {
  143 + return;
  144 + },
  145 + upCallback() {
  146 + const tbDeviceId = {
  147 + tbDeviceId: this.tbDeviceId
  148 + }
  149 + const condition = Object.values(this.formTime)
  150 + if (condition.length === 0) {
  151 + this.page.num += 1;
  152 + this.loadData(this.page.num, {
  153 + ...tbDeviceId
  154 + });
  155 + } else if (condition.filter(Boolean).length > 0) {
  156 + this.page.num += 1;
  157 + this.loadData(this.page.num, {
  158 + ...this.formTime,
  159 + ...tbDeviceId
  160 + });
  161 + } else {
  162 + this.page.num += 1;
  163 + this.loadData(this.page.num, {
  164 + ...tbDeviceId
  165 + });
  166 + }
  167 + },
  168 + async loadData(pageNo, params = {}) {
  169 + let httpData = {
  170 + ...params,
  171 + page: pageNo,
  172 + pageSize: 10
  173 + };
  174 + const res = await api.deviceApi.getRpcRecord({
  175 + params: httpData,
  176 + custom: {
  177 + load: false
  178 + }
  179 + })
  180 + if (!res) return
  181 + this.total = res.total;
  182 + uni.stopPullDownRefresh();
  183 + this.mescroll.endByPage(res.items.length, res.total);
  184 + if (pageNo == 1) {
  185 + this.list = res.items;
  186 + } else {
  187 + this.list = this.list.concat(res.items);
  188 + }
  189 + },
  190 + handleClickTag(currentIndex, list) {
  191 + list.map((item, index) => {
  192 + item.checked = index === currentIndex;
  193 + });
  194 + },
  195 + resetFilter() {
  196 + const {
  197 + issueStatus
  198 + } = this;
  199 + issueStatus.forEach(item => item.checked = false)
  200 + issueStatus[0].checked = true
  201 + },
  202 + close() {
  203 + this.show = false;
  204 + },
  205 + openSearchDialog() {
  206 + this.show = true;
  207 + this.resetFilter()
  208 + this.range = []
  209 + for (let i in this.formTime) Reflect.set(this.formTime, i, '')
  210 + },
  211 + hideKeyboard() {
  212 + uni.hideKeyboard();
  213 + },
  214 + confirmFilter() {
  215 + this.formTime.startTime = this.range[0]
  216 + this.formTime.endTime = this.range[1]
  217 + const issueStatus = this.issueStatus.find(item => item.checked);
  218 + this.formTime.status = issueStatus.type ? issueStatus.type : undefined,
  219 + this.loadData(1, {
  220 + tbDeviceId: this.tbDeviceId,
  221 + ...this.formTime
  222 + });
  223 + this.show = false;
  224 + },
  225 + openCommandDetail(item) {
  226 + useNavigateTo('/device-subpackage/device-detail/components/command-detail?data=', item)
  227 + }
  228 + }
  229 + };
  230 +</script>
  231 +
  232 +
  233 +<style lang="scss" scoped>
  234 + @import "../static/command-record.scss";
  235 +</style>
\ No newline at end of file
... ...
device-subpackage/device-detail/components/history-data.vue renamed from deviceSubPage/deviceDetailPage/tabDetail/historyData.vue
1   -<template>
2   - <view class="historyData">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <view class="historyData-top">
6   - <u-form :label-style="{ 'font-size': '0rpx' }">
7   - <u-form-item @click="openCalendar">
8   - <u-input v-model="timeData.selectTime" disabled disabledColor="#fff" placeholder="请选择日期"
9   - border="none" suffixIcon="arrow-down">
10   - <template slot="prefix">
11   - <image class="icon" src="../../../static/can-der.png"></image>
12   - </template>
13   - </u-input>
14   - </u-form-item>
15   - <u-form-item @click="openTimeGap">
16   - <u-input v-model="timeData.getTimeGap" disabled disabledColor="#fff" placeholder="请选择时间区间"
17   - border="none" suffixIcon="arrow-down">
18   - <template slot="prefix">
19   - <image class="icon" src="../../../static/time.png"></image>
20   - </template>
21   - </u-input>
22   - </u-form-item>
23   - <u-form-item @click="openAvg">
24   - <u-input shape="circle" v-model="aggText" placeholder="请选择数据聚合功能" disabled disabledColor="#377DFF0D"
25   - suffixIcon="arrow-down" />
26   - </u-form-item>
27   - <u-form-item @click="openTimeGap" v-if="limitFlag">
28   - <view class="u-flex">
29   - <text>最大条数</text>
30   - <u-number-box style="margin-left:30rpx" class="ml-10" v-model="timeData.limit" :min="7"
31   - :max="50000"></u-number-box>
32   - </view>
33   - </u-form-item>
34   - <u-form-item @click="openType">
35   - <u-input shape="circle" v-model="timeData.getType" placeholder="请选择属性" disabled
36   - disabledColor="#377DFF0D" suffixIcon="arrow-down" />
37   - </u-form-item>
38   - </u-form>
39   -
40   - <view class="charts-box" v-show="historyData.length">
41   - <qiun-data-charts type="area" canvas2d canvasId="daskujdhasljkdcnzjkdfhuoqwlqwjhkdsamjczxnmdasd123321"
42   - :chartData="chartData" :ontouch="true"
43   - :opts="{ xAxis: { disabled: true, itemCount: 6, scrollShow: true }, legend: { show: false }, enableScroll: true }" />
44   - </view>
45   - <mescroll-empty v-if="!historyData.length" />
46   - </view>
47   - <view class="historyData-bottom" v-show="historyData.length">
48   - <view class="table">
49   - <view class="tr bg-w" v-if="historyData.length">
50   - <view class="th">变量值</view>
51   - <view class="th">更新时间</view>
52   - </view>
53   - <view class="tr bg-g" :class="{ odd: index % 2 === 0 }" v-for="(item, index) in historyData"
54   - :key="index">
55   - <view class="td">{{ item.value }}</view>
56   - <view class="td">{{ item.ts }}</view>
57   - </view>
58   - </view>
59   - </view>
60   - <u-calendar :show="showCalendar" :defaultDate="defaultDate" closeOnClickOverlay mode="range" startText="开始时间"
61   - endText="结束时间" confirmDisabledText="请选择日期" :minDate="minDate" :maxDate="maxDate" @confirm="calendarConfirm"
62   - @close="calendarClose"></u-calendar>
63   - <u-picker :show="showTimeGap" :columns="columns" keyName="label" closeOnClickOverlay @confirm="confirmTimeGap"
64   - @cancel="cancelTimeGap" @close="cancelTimeGap" :defaultIndex="[3]"></u-picker>
65   - <u-picker :show="showSelectType" :columns="keys" closeOnClickOverlay @confirm="confirmTypeGap"
66   - @cancel="cancelTypeGap" @close="cancelTypeGap"></u-picker>
67   - <u-picker :show="showSelectAvg" :columns="avgColumns" keyName="label" closeOnClickOverlay
68   - @confirm="confirmAvgGap" @cancel="showSelectAvg=false" @close="showSelectAvg=false"></u-picker>
69   - </view>
70   -</template>
71   -
72   -<script>
73   - import fTabbar from '@/components/module/f-tabbar/f-tabbar';
74   - import qiunDataCharts from '@/uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue';
75   - import {
76   - getHistoryData
77   - } from '../api/index.js';
78   - import {
79   - formatToDate
80   - } from '@/plugins/utils.js';
81   - const d = new Date();
82   - const year = d.getFullYear();
83   - let month = d.getMonth() + 1;
84   - month = month < 10 ? `0${month}` : month;
85   - const date = d.getDate();
86   - export default {
87   - components: {
88   - fTabbar,
89   - qiunDataCharts
90   - },
91   - props: {
92   - keys: {
93   - type: Array,
94   - default: () => []
95   - },
96   - yesterday: {
97   - type: String,
98   - default: ''
99   - },
100   - today: {
101   - type: String,
102   - default: ''
103   - },
104   - timeDiff: {
105   - type: String,
106   - default: ''
107   - },
108   - historyData: {
109   - type: Array,
110   - default: () => []
111   - },
112   - entityId: {
113   - type: String,
114   - required: true
115   - },
116   - start: {
117   - type: String,
118   - required: true
119   - },
120   - end: {
121   - type: String,
122   - required: true
123   - }
124   - },
125   - data() {
126   - return {
127   - limitFlag: true,
128   - avgColumns: [
129   - [{
130   - label: '最小值',
131   - value: 'MIN'
132   - }, {
133   - label: '最大值',
134   - value: 'MAX'
135   - },
136   - {
137   - label: '平均值',
138   - value: 'AVG'
139   - },
140   - {
141   - label: '求和',
142   - value: 'SUM'
143   - },
144   - {
145   - label: '计数',
146   - value: 'COUNT'
147   - },
148   - {
149   - label: '空',
150   - value: 'NONE'
151   - },
152   - ]
153   - ],
154   - startTs: this.start,
155   - endTs: this.end,
156   - showCalendar: false,
157   - showTimeGap: false,
158   - showSelectType: false,
159   - showSelectAvg: false,
160   - minDate: `${year}-${month - 1}-${date}`,
161   - maxDate: `${year}-${month}-${date + 1}`,
162   - defaultDate: [this.yesterday, this.today],
163   - chartData: {
164   - categories: this.historyData.length && this.historyData.map(item => item.ts),
165   - series: [{
166   - name: this.keys[0][0],
167   - data: this.historyData.length && this.historyData.map(item => Number(item.value))
168   - }]
169   - },
170   - columns: [
171   - [{
172   - label: '5分钟',
173   - value: 300000
174   - },
175   - {
176   - label: '10分钟',
177   - value: 600000
178   - },
179   - {
180   - label: '15分钟',
181   - value: 900000
182   - },
183   - {
184   - label: '30分钟',
185   - value: 1800000
186   - },
187   - {
188   - label: '1小时',
189   - value: 3600000
190   - },
191   - {
192   - label: '2小时',
193   - value: 7200000
194   - }
195   - ]
196   - ],
197   - timeData: {
198   - selectTime: this.yesterday + ' 至 ' + this.today,
199   - getTimeGap: this.timeDiff,
200   - getType: this.keys[0][0],
201   - limit: 7,
202   - agg: 'NONE'
203   - },
204   - aggText: '空'
205   - };
206   - },
207   - watch: {
208   - historyData(newValue) {
209   - if (!newValue.length) {
210   - this.chartData.categories = [];
211   - this.chartData.series = [];
212   - } else {
213   - this.chartData.categories = newValue.map(item => item.ts);
214   - this.chartData.series = [{
215   - name: this.keys[0][0],
216   - data: newValue.map(item => Number(item.value))
217   - }];
218   - }
219   - }
220   - },
221   - methods: {
222   - // 动态生成Columns
223   - generateColumns(value) {
224   - if (value < 604800000) {
225   - // 小于7天
226   - return [
227   - [{
228   - label: '5分钟',
229   - value: 300000
230   - },
231   - {
232   - label: '10分钟',
233   - value: 600000
234   - },
235   - {
236   - label: '15分钟',
237   - value: 900000
238   - },
239   - {
240   - label: '30分钟',
241   - value: 1800000
242   - },
243   - {
244   - label: '1小时',
245   - value: 3600000
246   - },
247   - {
248   - label: '2小时',
249   - value: 7200000
250   - }
251   - ]
252   - ];
253   - } else if (value < 2592000000) {
254   - // 小于30天
255   - return [
256   - [{
257   - label: '30分钟',
258   - value: 1800000
259   - },
260   - {
261   - label: '1小时',
262   - value: 3600000
263   - },
264   - {
265   - label: '2小时',
266   - value: 7200000
267   - },
268   - {
269   - label: '5小时',
270   - value: 18000000
271   - },
272   - {
273   - label: '10小时',
274   - value: 36000000
275   - },
276   - {
277   - label: '12小时',
278   - value: 43200000
279   - },
280   - {
281   - label: '1天',
282   - value: 86400000
283   - }
284   - ]
285   - ];
286   - } else if (value >= 2592000000) {
287   - // 大于30天
288   - return [
289   - [{
290   - label: '2小时',
291   - value: 7200000
292   - },
293   - {
294   - label: '5小时',
295   - value: 18000000
296   - },
297   - {
298   - label: '10小时',
299   - value: 36000000
300   - },
301   - {
302   - label: '12小时',
303   - value: 43200000
304   - },
305   - {
306   - label: '1天',
307   - value: 86400000
308   - }
309   - ]
310   - ];
311   - }
312   - },
313   - openCalendar() {
314   - this.showCalendar = true;
315   - },
316   - openTimeGap() {
317   - this.showTimeGap = true;
318   - },
319   - openType() {
320   - this.showSelectType = true;
321   - },
322   - openAvg() {
323   - this.showSelectAvg = true
324   - },
325   - calendarConfirm(date) {
326   - this.showCalendar = false;
327   - this.timeData.selectTime = `${date[0]} 至 ${date[date.length - 1]}`;
328   - // 选择的日期时间差(时间戳)
329   - const timeDiff = formatToDate(date[date.length - 1], 'x') - formatToDate(date[0], 'x');
330   - const genColumns = this.generateColumns(timeDiff);
331   - this.columns = genColumns;
332   - this.timeData.getTimeGap = '';
333   - this.timeData.getType = '';
334   - this.startTs = formatToDate(date[0], 'x');
335   - // 最后时间的最后一秒
336   - this.endTs = formatToDate(`${date[date.length - 1]} 23:59:59`, 'x');
337   - },
338   - calendarClose() {
339   - this.showCalendar = false;
340   - },
341   - confirmTimeGap(time) {
342   - this.showTimeGap = false;
343   - this.timeData.getTimeGap = time.value[0].label;
344   - this.timeData.getType = '';
345   - },
346   -
347   - cancelTimeGap() {
348   - this.showTimeGap = false;
349   - },
350   - confirmAvgGap(e) {
351   - this.timeData.agg = e.value[0].value
352   - this.aggText = e.value[0].label
353   - if (e.value[0].value === 'NONE') {
354   - this.limitFlag = true
355   - this.timeData.limit = 7
356   - } else {
357   - this.timeData.limit = null
358   - this.limitFlag = false
359   - }
360   - this.showSelectAvg = false
361   - },
362   - async confirmTypeGap(time) {
363   - this.showSelectType = false;
364   - this.timeData.getType = time.value[0];
365   - const interval = this.columns[0].find(item => item.label === this.timeData.getTimeGap);
366   - const data = await getHistoryData({
367   - startTs: this.startTs,
368   - endTs: this.endTs,
369   - keys: this.timeData.getType,
370   - interval: this.limitFlag ? null : interval.value,
371   - entityId: this.entityId,
372   - limit: this.timeData.limit,
373   - agg: this.timeData.agg
374   - });
375   - this.$emit('update', data[this.timeData.getType]);
376   - },
377   - cancelTypeGap() {
378   - this.showSelectType = false;
379   - }
380   - }
381   - };
382   -</script>
383   -
384   -<style lang="scss" scoped>
385   - .charts-box {
386   - width: 100%;
387   - height: 550rpx;
388   - }
389   -
390   - .historyData {
391   - margin: 30rpx;
392   -
393   - .historyData-top {
394   - padding: 30rpx;
395   - background-color: #fff;
396   - // height: 870rpx;
397   - border-radius: 20rpx;
398   -
399   - .icon {
400   - width: 28rpx;
401   - height: 28rpx;
402   - margin-right: 15rpx;
403   - }
404   - }
405   -
406   - .historyData-bottom {
407   - margin-top: 30rpx;
408   - background-color: #fff;
409   - border-radius: 20rpx;
410   -
411   - .table {
412   - border: 0px solid darkgray;
413   -
414   - .tr {
415   - display: flex;
416   - width: 100%;
417   - justify-content: center;
418   - height: 3rem;
419   - align-items: center;
420   -
421   - .th {
422   - display: flex;
423   - justify-content: center;
424   - align-items: center;
425   - width: 50%;
426   - color: #333;
427   - font-weight: 500;
428   - }
429   -
430   - .td {
431   - color: #999;
432   - width: 50%;
433   - display: flex;
434   - justify-content: center;
435   - text-align: center;
436   - }
437   - }
438   - }
439   - }
440   - }
441   -
442   - .odd {
443   - background-color: #f9fcff;
444   - }
  1 +<template>
  2 + <view class="historyData">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <view class="historyData-top">
  6 + <u-form :label-style="{ 'font-size': '0rpx' }">
  7 + <u-form-item @click="openCalendar">
  8 + <u-input v-model="timeData.selectTime" disabled disabledColor="#fff" placeholder="请选择日期"
  9 + border="none" suffixIcon="arrow-down">
  10 + <template slot="prefix">
  11 + <image class="icon" src="../../../static/can-der.png"></image>
  12 + </template>
  13 + </u-input>
  14 + </u-form-item>
  15 + <u-form-item @click="openTimeGap">
  16 + <u-input v-model="timeData.getTimeGap" disabled disabledColor="#fff" placeholder="请选择时间区间"
  17 + border="none" suffixIcon="arrow-down">
  18 + <template slot="prefix">
  19 + <image class="icon" src="../../../static/time.png"></image>
  20 + </template>
  21 + </u-input>
  22 + </u-form-item>
  23 + <u-form-item @click="openAvg">
  24 + <u-input shape="circle" v-model="aggText" placeholder="请选择数据聚合功能" disabled disabledColor="#377DFF0D"
  25 + suffixIcon="arrow-down" />
  26 + </u-form-item>
  27 + <u-form-item @click="openTimeGap" v-if="limitFlag">
  28 + <view class="u-flex">
  29 + <text>最大条数</text>
  30 + <u-number-box style="margin-left:30rpx" class="ml-10" v-model="timeData.limit" :min="7"
  31 + :max="50000"></u-number-box>
  32 + </view>
  33 + </u-form-item>
  34 + <u-form-item @click="openType">
  35 + <u-input shape="circle" v-model="timeData.getType" placeholder="请选择属性" disabled
  36 + disabledColor="#377DFF0D" suffixIcon="arrow-down" />
  37 + </u-form-item>
  38 + </u-form>
  39 +
  40 + <view class="charts-box" v-show="historyData.length">
  41 + <qiun-data-charts type="area" canvas2d canvasId="daskujdhasljkdcnzjkdfhuoqwlqwjhkdsamjczxnmdasd123321"
  42 + :chartData="chartData" :ontouch="true"
  43 + :opts="{ xAxis: { disabled: true, itemCount: 6, scrollShow: true }, legend: { show: false }, enableScroll: true }" />
  44 + </view>
  45 + <mescroll-empty v-if="!historyData.length" />
  46 + </view>
  47 + <view class="historyData-bottom" v-show="historyData.length">
  48 + <view class="table">
  49 + <view class="tr bg-w" v-if="historyData.length">
  50 + <view class="th">变量值</view>
  51 + <view class="th">更新时间</view>
  52 + </view>
  53 + <view class="tr bg-g" :class="{ odd: index % 2 === 0 }" v-for="(item, index) in historyData"
  54 + :key="index">
  55 + <view class="td">{{ item.value }}</view>
  56 + <view class="td">{{ item.ts }}</view>
  57 + </view>
  58 + </view>
  59 + </view>
  60 + <u-calendar :show="showCalendar" :defaultDate="defaultDate" closeOnClickOverlay mode="range" startText="开始时间"
  61 + endText="结束时间" confirmDisabledText="请选择日期" :minDate="minDate" :maxDate="maxDate" @confirm="calendarConfirm"
  62 + @close="calendarClose"></u-calendar>
  63 + <u-picker :show="showTimeGap" :columns="columns" keyName="label" closeOnClickOverlay @confirm="confirmTimeGap"
  64 + @cancel="cancelTimeGap" @close="cancelTimeGap" :defaultIndex="[3]"></u-picker>
  65 + <u-picker :show="showSelectType" :columns="keys" closeOnClickOverlay @confirm="confirmTypeGap"
  66 + @cancel="cancelTypeGap" @close="cancelTypeGap"></u-picker>
  67 + <u-picker :show="showSelectAvg" :columns="avgColumns" keyName="label" closeOnClickOverlay
  68 + @confirm="confirmAvgGap" @cancel="showSelectAvg=false" @close="showSelectAvg=false"></u-picker>
  69 + </view>
  70 +</template>
  71 +
  72 +<script>
  73 + import fTabbar from '@/components/module/f-tabbar/f-tabbar';
  74 + import qiunDataCharts from './uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue';
  75 + import {
  76 + getHistoryData
  77 + } from '../api/index.js';
  78 + import {
  79 + formatToDate
  80 + } from '@/plugins/utils.js';
  81 + const d = new Date();
  82 + const year = d.getFullYear();
  83 + let month = d.getMonth() + 1;
  84 + month = month < 10 ? `0${month}` : month;
  85 + const date = d.getDate();
  86 + export default {
  87 + components: {
  88 + fTabbar,
  89 + qiunDataCharts
  90 + },
  91 + props: {
  92 + keys: {
  93 + type: Array,
  94 + default: () => []
  95 + },
  96 + yesterday: {
  97 + type: String,
  98 + default: ''
  99 + },
  100 + today: {
  101 + type: String,
  102 + default: ''
  103 + },
  104 + timeDiff: {
  105 + type: String,
  106 + default: ''
  107 + },
  108 + historyData: {
  109 + type: Array,
  110 + default: () => []
  111 + },
  112 + entityId: {
  113 + type: String,
  114 + required: true
  115 + },
  116 + start: {
  117 + type: String,
  118 + required: true
  119 + },
  120 + end: {
  121 + type: String,
  122 + required: true
  123 + }
  124 + },
  125 + data() {
  126 + return {
  127 + limitFlag: true,
  128 + avgColumns: [
  129 + [{
  130 + label: '最小值',
  131 + value: 'MIN'
  132 + }, {
  133 + label: '最大值',
  134 + value: 'MAX'
  135 + },
  136 + {
  137 + label: '平均值',
  138 + value: 'AVG'
  139 + },
  140 + {
  141 + label: '求和',
  142 + value: 'SUM'
  143 + },
  144 + {
  145 + label: '计数',
  146 + value: 'COUNT'
  147 + },
  148 + {
  149 + label: '空',
  150 + value: 'NONE'
  151 + },
  152 + ]
  153 + ],
  154 + startTs: this.start,
  155 + endTs: this.end,
  156 + showCalendar: false,
  157 + showTimeGap: false,
  158 + showSelectType: false,
  159 + showSelectAvg: false,
  160 + minDate: `${year}-${month - 1}-${date}`,
  161 + maxDate: `${year}-${month}-${date + 1}`,
  162 + defaultDate: [this.yesterday, this.today],
  163 + chartData: {
  164 + categories: this.historyData.length && this.historyData.map(item => item.ts),
  165 + series: [{
  166 + name: this.keys[0][0],
  167 + data: this.historyData.length && this.historyData.map(item => Number(item.value))
  168 + }]
  169 + },
  170 + columns: [
  171 + [{
  172 + label: '5分钟',
  173 + value: 300000
  174 + },
  175 + {
  176 + label: '10分钟',
  177 + value: 600000
  178 + },
  179 + {
  180 + label: '15分钟',
  181 + value: 900000
  182 + },
  183 + {
  184 + label: '30分钟',
  185 + value: 1800000
  186 + },
  187 + {
  188 + label: '1小时',
  189 + value: 3600000
  190 + },
  191 + {
  192 + label: '2小时',
  193 + value: 7200000
  194 + }
  195 + ]
  196 + ],
  197 + timeData: {
  198 + selectTime: this.yesterday + ' 至 ' + this.today,
  199 + getTimeGap: this.timeDiff,
  200 + getType: this.keys[0][0],
  201 + limit: 7,
  202 + agg: 'NONE'
  203 + },
  204 + aggText: '空'
  205 + };
  206 + },
  207 + watch: {
  208 + historyData(newValue) {
  209 + if (!newValue.length) {
  210 + this.chartData.categories = [];
  211 + this.chartData.series = [];
  212 + } else {
  213 + this.chartData.categories = newValue.map(item => item.ts);
  214 + this.chartData.series = [{
  215 + name: this.keys[0][0],
  216 + data: newValue.map(item => Number(item.value))
  217 + }];
  218 + }
  219 + }
  220 + },
  221 + methods: {
  222 + // 动态生成Columns
  223 + generateColumns(value) {
  224 + if (value < 604800000) {
  225 + // 小于7天
  226 + return [
  227 + [{
  228 + label: '5分钟',
  229 + value: 300000
  230 + },
  231 + {
  232 + label: '10分钟',
  233 + value: 600000
  234 + },
  235 + {
  236 + label: '15分钟',
  237 + value: 900000
  238 + },
  239 + {
  240 + label: '30分钟',
  241 + value: 1800000
  242 + },
  243 + {
  244 + label: '1小时',
  245 + value: 3600000
  246 + },
  247 + {
  248 + label: '2小时',
  249 + value: 7200000
  250 + }
  251 + ]
  252 + ];
  253 + } else if (value < 2592000000) {
  254 + // 小于30天
  255 + return [
  256 + [{
  257 + label: '30分钟',
  258 + value: 1800000
  259 + },
  260 + {
  261 + label: '1小时',
  262 + value: 3600000
  263 + },
  264 + {
  265 + label: '2小时',
  266 + value: 7200000
  267 + },
  268 + {
  269 + label: '5小时',
  270 + value: 18000000
  271 + },
  272 + {
  273 + label: '10小时',
  274 + value: 36000000
  275 + },
  276 + {
  277 + label: '12小时',
  278 + value: 43200000
  279 + },
  280 + {
  281 + label: '1天',
  282 + value: 86400000
  283 + }
  284 + ]
  285 + ];
  286 + } else if (value >= 2592000000) {
  287 + // 大于30天
  288 + return [
  289 + [{
  290 + label: '2小时',
  291 + value: 7200000
  292 + },
  293 + {
  294 + label: '5小时',
  295 + value: 18000000
  296 + },
  297 + {
  298 + label: '10小时',
  299 + value: 36000000
  300 + },
  301 + {
  302 + label: '12小时',
  303 + value: 43200000
  304 + },
  305 + {
  306 + label: '1天',
  307 + value: 86400000
  308 + }
  309 + ]
  310 + ];
  311 + }
  312 + },
  313 + openCalendar() {
  314 + this.showCalendar = true;
  315 + },
  316 + openTimeGap() {
  317 + this.showTimeGap = true;
  318 + },
  319 + openType() {
  320 + this.showSelectType = true;
  321 + },
  322 + openAvg() {
  323 + this.showSelectAvg = true
  324 + },
  325 + calendarConfirm(date) {
  326 + this.showCalendar = false;
  327 + this.timeData.selectTime = `${date[0]} 至 ${date[date.length - 1]}`;
  328 + // 选择的日期时间差(时间戳)
  329 + const timeDiff = formatToDate(date[date.length - 1], 'x') - formatToDate(date[0], 'x');
  330 + const genColumns = this.generateColumns(timeDiff);
  331 + this.columns = genColumns;
  332 + this.timeData.getTimeGap = '';
  333 + this.timeData.getType = '';
  334 + this.startTs = formatToDate(date[0], 'x');
  335 + // 最后时间的最后一秒
  336 + this.endTs = formatToDate(`${date[date.length - 1]} 23:59:59`, 'x');
  337 + },
  338 + calendarClose() {
  339 + this.showCalendar = false;
  340 + },
  341 + confirmTimeGap(time) {
  342 + this.showTimeGap = false;
  343 + this.timeData.getTimeGap = time.value[0].label;
  344 + this.timeData.getType = '';
  345 + },
  346 +
  347 + cancelTimeGap() {
  348 + this.showTimeGap = false;
  349 + },
  350 + confirmAvgGap(e) {
  351 + this.timeData.agg = e.value[0].value
  352 + this.aggText = e.value[0].label
  353 + if (e.value[0].value === 'NONE') {
  354 + this.limitFlag = true
  355 + this.timeData.limit = 7
  356 + } else {
  357 + this.timeData.limit = null
  358 + this.limitFlag = false
  359 + }
  360 + this.showSelectAvg = false
  361 + },
  362 + async confirmTypeGap(time) {
  363 + this.showSelectType = false;
  364 + this.timeData.getType = time.value[0];
  365 + const interval = this.columns[0].find(item => item.label === this.timeData.getTimeGap);
  366 + const data = await getHistoryData({
  367 + startTs: this.startTs,
  368 + endTs: this.endTs,
  369 + keys: this.timeData.getType,
  370 + interval: this.limitFlag ? null : interval.value,
  371 + entityId: this.entityId,
  372 + limit: this.timeData.limit,
  373 + agg: this.timeData.agg
  374 + });
  375 + this.$emit('update', data[this.timeData.getType]);
  376 + },
  377 + cancelTypeGap() {
  378 + this.showSelectType = false;
  379 + }
  380 + }
  381 + };
  382 +</script>
  383 +
  384 +<style lang="scss" scoped>
  385 + .charts-box {
  386 + width: 100%;
  387 + height: 550rpx;
  388 + }
  389 +
  390 + .historyData {
  391 + margin: 30rpx;
  392 +
  393 + .historyData-top {
  394 + padding: 30rpx;
  395 + background-color: #fff;
  396 + // height: 870rpx;
  397 + border-radius: 20rpx;
  398 +
  399 + .icon {
  400 + width: 28rpx;
  401 + height: 28rpx;
  402 + margin-right: 15rpx;
  403 + }
  404 + }
  405 +
  406 + .historyData-bottom {
  407 + margin-top: 30rpx;
  408 + background-color: #fff;
  409 + border-radius: 20rpx;
  410 +
  411 + .table {
  412 + border: 0px solid darkgray;
  413 +
  414 + .tr {
  415 + display: flex;
  416 + width: 100%;
  417 + justify-content: center;
  418 + height: 3rem;
  419 + align-items: center;
  420 +
  421 + .th {
  422 + display: flex;
  423 + justify-content: center;
  424 + align-items: center;
  425 + width: 50%;
  426 + color: #333;
  427 + font-weight: 500;
  428 + }
  429 +
  430 + .td {
  431 + color: #999;
  432 + width: 50%;
  433 + display: flex;
  434 + justify-content: center;
  435 + text-align: center;
  436 + }
  437 + }
  438 + }
  439 + }
  440 + }
  441 +
  442 + .odd {
  443 + background-color: #f9fcff;
  444 + }
445 445 </style>
... ...
  1 +<template>
  2 + <view class="mp-u-modal">
  3 + <u-modal :mask-close-able="true" :show="showModal" closeOnClickOverlay :showConfirmButton="false"
  4 + @close="$emit('hideModal')" @touchmove.stop.prevent="disabledScroll" z-index="99999">
  5 + <view class="w-100 modal-content">
  6 + <view class="header-title">命令下发</view>
  7 + <view class="u-flex">
  8 + <text class="type-text">下发类型:</text>
  9 + <u-radio-group v-model="commandType" placement="row">
  10 + <u-radio activeColor="#3388FF" label="单向" name="OneWay"></u-radio>
  11 + <view style="margin: 0 20rpx;"></view>
  12 + <u-radio activeColor="#3388FF" label="双向" name="TwoWay"></u-radio>
  13 + </u-radio-group>
  14 + </view>
  15 + <view class="content-body">
  16 + <div class="u-flex u-row-between">
  17 + <u--textarea :placeholder="`请输入下发内容${isShowTCP?'(字符串格式)':'(json格式)'}`"
  18 + v-model="inputCommandVal" />
  19 + <u-icon v-if="!isShowTCP" @click="handleCopy(copyTextValue)" name="question-circle"
  20 + color="#2979ff" size="28" class="ml-10">
  21 + </u-icon>
  22 + </div>
  23 + </view>
  24 + <view class="button-group">
  25 + <view>
  26 + <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="取消"
  27 + @click="cancelCommand"></u-button>
  28 + </view>
  29 + <view>
  30 + <u-button color="#3388ff" shape="circle" text="确认" @click="confirmCommand"></u-button>
  31 + </view>
  32 + </view>
  33 + </view>
  34 + </u-modal>
  35 + </view>
  36 +</template>
  37 +
  38 +<script>
  39 + import {
  40 + useShowModal
  41 + } from '@/plugins/utils.js'
  42 +
  43 + export default {
  44 + props: {
  45 + showModal: Boolean,
  46 + isShowTCP: Boolean
  47 + },
  48 + data() {
  49 + return {
  50 + current: 0,
  51 + commandType: 'OneWay',
  52 + inputCommandVal: '',
  53 + copyTextValue: {
  54 + "method": "methodThingskit",
  55 + "params": {
  56 + "pin": 7,
  57 + "value": 1
  58 + }
  59 + }
  60 + }
  61 + },
  62 + methods: {
  63 + cancelCommand() {
  64 + this.$emit('cancelCommand')
  65 + },
  66 + confirmCommand() {
  67 + this.$emit('confirmCommand', this.commandType, this.inputCommandVal)
  68 + },
  69 + handleCopy(value) {
  70 + useShowModal(JSON.stringify(value), '命令下发', '复制内容').then(res => {
  71 + uni.setClipboardData({
  72 + data: JSON.stringify(value),
  73 + success: () => {
  74 + uni.showToast({
  75 + title: '复制成功'
  76 + })
  77 + }
  78 + });
  79 + })
  80 + },
  81 + reset() {
  82 + this.commandType = 'OneWay'
  83 + this.inputCommandVal = ''
  84 + }
  85 + }
  86 + }
  87 +</script>
  88 +
  89 +<style lang="scss" scoped>
  90 + .modal-content {
  91 + width: 720rpx;
  92 + padding: 0 30rpx;
  93 + background-color: white;
  94 +
  95 + .header-title {
  96 + text-align: center;
  97 + font-weight: 700;
  98 + margin-bottom: 40rpx;
  99 + }
  100 +
  101 + .type-text {
  102 + color: #333;
  103 + font-size: 14px;
  104 + font-weight: 700;
  105 + margin-right: 30rpx;
  106 + }
  107 +
  108 + .content-body {
  109 + margin-top: 28rpx;
  110 + width: 100%;
  111 + }
  112 +
  113 + .button-group {
  114 + display: flex;
  115 + margin-top: 40rpx;
  116 + justify-content: space-between;
  117 +
  118 + view {
  119 + width: 300rpx;
  120 + }
  121 + }
  122 + }
  123 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <view class="realtime-page">
  3 + <view class="item" v-for="(item, index) in recordList" :key="index">
  4 + <view class="item-top">
  5 + <view>{{ item.key }}</view>
  6 + <view class="item-value">{{ item.value }}</view>
  7 + </view>
  8 + <view class="item-time">{{ item.time }}</view>
  9 + </view>
  10 + <mescroll-empty v-if="!recordList.length" />
  11 + </view>
  12 +</template>
  13 +
  14 +<script>
  15 + export default {
  16 + props: {
  17 + recordList: {
  18 + type: Array,
  19 + default: () => []
  20 + }
  21 + }
  22 + };
  23 +</script>
  24 +
  25 +<style lang="scss" scoped>
  26 + @import "../static/realtime-data.scss";
  27 +</style>
\ No newline at end of file
... ...
  1 +const list = [{
  2 + name: "基础信息",
  3 + },
  4 + {
  5 + name: "实时数据",
  6 + },
  7 + {
  8 + name: "历史数据",
  9 + },
  10 + {
  11 + name: "告警记录",
  12 + },
  13 +]
  14 +
  15 +const issueStatus = [{
  16 + checked: true,
  17 + name: '全部',
  18 + type: ''
  19 + },
  20 + {
  21 + checked: false,
  22 + name: '队列中',
  23 + type: 'QUEUED'
  24 + },
  25 + {
  26 + checked: false,
  27 + name: '已发送',
  28 + type: 'SENT'
  29 + },
  30 + {
  31 + checked: false,
  32 + name: '发送成功',
  33 + type: 'DELIVERED'
  34 + },
  35 + {
  36 + checked: false,
  37 + name: '响应成功',
  38 + type: 'SUCCESSFUL'
  39 + },
  40 + {
  41 + checked: false,
  42 + name: '超时',
  43 + type: 'TIMEOUT'
  44 + },
  45 + {
  46 + checked: false,
  47 + name: '已过期',
  48 + type: 'EXPIRED'
  49 + },
  50 + {
  51 + checked: false,
  52 + name: '响应失败',
  53 + type: 'FAILED'
  54 + },
  55 + {
  56 + checked: false,
  57 + name: '已删除',
  58 + type: 'DELETED'
  59 + }
  60 +]
  61 +
  62 +const commandTypeList = [{
  63 + value: 'OneWay',
  64 + name: '单向',
  65 + },
  66 + {
  67 + value: 'TwoWay',
  68 + name: '双向'
  69 + },
  70 +]
  71 +
  72 +
  73 +export {
  74 + list,
  75 + issueStatus,
  76 + commandTypeList
  77 +}
\ No newline at end of file
... ...
device-subpackage/device-detail/device-detail.vue renamed from deviceSubPage/deviceDetailPage/deviceDetail.vue
1   -<template>
2   - <view class="device-detail-page">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <u-sticky bgColor="#fff">
6   - <u-tabs :list="list" :current="currentTab" @click="handleTabClick" :activeStyle="{
7   - fontWeight: 'bold',
8   - color: '#333',
9   - }" :inactiveStyle="{
10   - color: '#999',
11   - }" :scrollable="isScrollable" />
12   - </u-sticky>
13   - <view style="margin-top: 30rpx">
14   - <basicInfo v-show="currentTab == 0" :deviceDetail="deviceDetail" />
15   - <realTimeData v-show="currentTab === 1" :recordList="recordList" />
16   - <historyData v-if="currentTab === 2" :keys="keys" :yesterday="yesterday" :today="today" :timeDiff="timeDiff"
17   - :historyData="historyData" :entityId="entityId" :start="startTs" :end="endTs" @update="handleUpdate" />
18   - <alarmHistory v-show="currentTab === 3" :deviceId="deviceId" />
19   - <commondRecord v-if="currentTab === 4" :tbDeviceId="entityId" />
20   - </view>
21   - </view>
22   -</template>
23   -
24   -<script>
25   - import fTabbar from "@/components/module/f-tabbar/f-tabbar";
26   - import basicInfo from "./tabDetail/basicInfo.vue";
27   - import realTimeData from "./tabDetail/realtimeData.vue";
28   - import alarmHistory from "./tabDetail/alarmHistory.vue";
29   - import historyData from "./tabDetail/historyData.vue";
30   - import commondRecord from "./tabDetail/CommandRecord.vue";
31   - import {
32   - getDeviceKeys,
33   - getHistoryData
34   - } from "./api/index.js";
35   - import {
36   - formatToDate
37   - } from "@/plugins/utils.js";
38   - import MescrollCompMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mixins/mescroll-comp.js";
39   - import moment from "moment";
40   - import base from "@/config/baseUrl.js";
41   -
42   - export default {
43   - mixins: [MescrollCompMixin],
44   - components: {
45   - fTabbar,
46   - basicInfo,
47   - realTimeData,
48   - alarmHistory,
49   - historyData,
50   - commondRecord,
51   - },
52   - data() {
53   - return {
54   - list: [{
55   - name: "基础信息",
56   - },
57   - {
58   - name: "实时数据",
59   - },
60   - {
61   - name: "历史数据",
62   - },
63   - {
64   - name: "告警记录",
65   - },
66   - ],
67   - currentTab: 0,
68   - deviceId: "",
69   - deviceDetail: {},
70   - keys: [],
71   - yesterday: "",
72   - today: "",
73   - timeDiff: "",
74   - historyData: [],
75   - entityId: "",
76   - startTs: "",
77   - endTs: "",
78   - recordList: [],
79   - isScrollable: false,
80   - attrList: []
81   - };
82   - },
83   - onUnload() {
84   - // 页面关闭时,销毁webSocket连接,否则第二次会存在连接不到的情况
85   - uni.closeSocket();
86   - },
87   - async onLoad(options) {
88   - const {
89   - id,
90   - alarmStatus,
91   - lastOnlineTime,
92   - tbDeviceId,
93   - deviceProfileId
94   - } =
95   - options;
96   - this.deviceId = id;
97   - const res = await uni.$u.http.get(`/yt/device/${id}`);
98   - this.deviceDetail = {
99   - ...res,
100   - alarmStatus,
101   - lastOnlineTime,
102   - };
103   -
104   - // 设备类型不是网关子设备的添加一个命令记录的选项卡
105   - if (this.deviceDetail.deviceType !== "SENSOR") {
106   - this.list.push({
107   - name: "命令记录",
108   - });
109   - }
110   - this.isScrollable = this.list.length > 4;
111   - if (res.deviceProfileId) {
112   - const getAttrList = await uni.$u.http.get(
113   - `/yt/device/attributes/${res.deviceProfileId}`
114   - );
115   - if (Array.isArray(getAttrList)) {
116   - this.attrList = getAttrList.map(m => {
117   - return m.identifier
118   - })
119   - }
120   - }
121   - // 连接webSockte
122   - const socketTask = uni.connectSocket({
123   - url: `${base.socketPrefix}://${base.baseWebSocketUrl}/api/ws/plugins/telemetry?token=` +
124   - uni.getStorageSync("userInfo").isToken, //仅为示例,并非真实接口地址。
125   - complete: () => {},
126   - });
127   - uni.onSocketOpen((header) => {
128   - socketTask.send({
129   - data: JSON.stringify({
130   - attrSubCmds: [],
131   - tsSubCmds: [{
132   - entityType: "DEVICE",
133   - entityId: tbDeviceId,
134   - scope: "LATEST_TELEMETRY",
135   - cmdId: 1,
136   - keys: this.attrList.join(','),
137   - }, ],
138   - historyCmds: [],
139   - entityDataCmds: [],
140   - entityDataUnsubscribeCmds: [],
141   - alarmDataCmds: [],
142   - alarmDataUnsubscribeCmds: [],
143   - entityCountCmds: [],
144   - entityCountUnsubscribeCmds: [],
145   - }),
146   - success() {},
147   - });
148   - });
149   - socketTask.onMessage((msg) => {
150   - const {
151   - data
152   - } = JSON.parse(msg.data);
153   - const newArray = [];
154   - for (const key in data) {
155   - const [time, value] = data[key].flat(1);
156   - let obj = {
157   - key,
158   - time,
159   - value,
160   - };
161   - if (this.recordList.length === 0) {
162   - this.recordList.unshift(obj);
163   - } else {
164   - newArray.push(obj);
165   - }
166   - }
167   - newArray.forEach((item) => {
168   - let flag = false;
169   - this.recordList.forEach((item1) => {
170   - if (item1.key === item.key) {
171   - item1.value = item.value;
172   - item1.time = item.time;
173   - flag = true;
174   - }
175   - });
176   - if (!flag) {
177   - this.recordList.unshift(item);
178   - }
179   - });
180   - this.recordList = this.recordList.map((item) => {
181   - return {
182   - ...item,
183   - time: formatToDate(item.time, "YYYY-MM-DD HH:mm:ss"),
184   - };
185   - });
186   - });
187   -
188   - const keys = await getDeviceKeys(tbDeviceId);
189   - this.keys = [keys];
190   - // 昨天
191   - this.yesterday = moment().subtract(1, "days").format("YYYY-MM-DD");
192   - // 今天
193   - this.today = moment().format("YYYY-MM-DD");
194   - // 开始时间
195   - this.startTs = moment().subtract(1, "days").format("x");
196   - // 结束时间
197   - this.endTs = moment().format("x");
198   - this.entityId = tbDeviceId;
199   -
200   - const data = await getHistoryData({
201   - entityId: tbDeviceId,
202   - startTs: this.startTs,
203   - endTs: this.endTs,
204   - keys: keys[0],
205   - // interval: 1800000,
206   - limit: 7,
207   - agg: 'NONE'
208   - });
209   - this.timeDiff = "30分钟";
210   - if (!Object.keys(data).length) return;
211   -
212   - this.historyData = data[keys[0]].map((item) => {
213   - return {
214   - value: item.value,
215   - ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
216   - };
217   - });
218   - },
219   - methods: {
220   - handleTabClick({
221   - index
222   - }) {
223   - this.currentTab = index;
224   - },
225   - handleUpdate(data, e) {
226   - if (!Array.isArray(data)) {
227   - this.historyData = [];
228   - return;
229   - }
230   - this.historyData = data.map((item) => {
231   - return {
232   - value: item.value,
233   - ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
234   - };
235   - });
236   - },
237   - },
238   - };
239   -</script>
240   -
241   -<style lang="scss" scoped>
242   - .device-detail-page {
243   - height: 100vh;
244   - background-color: #f8f9fa;
245   - }
246   -</style>
  1 +<template>
  2 + <view class="device-detail-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <u-sticky :bgColor="bgColor">
  6 + <u-tabs :list="list" :current="currentTab" @click="handleTabClick" :activeStyle="activeColor"
  7 + :inactiveStyle="inActiveColor" :scrollable="isScrollable" />
  8 + </u-sticky>
  9 + <view class="mt-3">
  10 + <basic-info v-show="currentTab == 0" :deviceDetail="deviceDetail" />
  11 + <realtime-data v-show="currentTab === 1" :recordList="recordList" />
  12 + <history-data v-if="currentTab === 2" :keys="keys" :yesterday="yesterday" :today="today"
  13 + :timeDiff="timeDiff" :historyData="historyData" :entityId="entityId" :start="startTs" :end="endTs"
  14 + @update="handleUpdate" />
  15 + <alarm-history v-show="currentTab === 3" :deviceId="deviceId" />
  16 + <commond-record v-if="currentTab === 4" :tbDeviceId="entityId" />
  17 + </view>
  18 + </view>
  19 +</template>
  20 +
  21 +<script>
  22 + import fTabbar from "@/components/module/f-tabbar/f-tabbar";
  23 + import basicInfo from "./components/basic-info.vue";
  24 + import realtimeData from "./components/realtime-data.vue";
  25 + import alarmHistory from "./components/alarm-history.vue";
  26 + import historyData from "./components/history-data.vue";
  27 + import commondRecord from "./components/command-record.vue";
  28 + import { getDeviceKeys,getHistoryData } from "./api/index.js";
  29 + import {formatToDate} from "@/plugins/utils.js";
  30 + import MescrollCompMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mixins/mescroll-comp.js";
  31 + import moment from "moment";
  32 + import base from "@/config/baseUrl.js";
  33 + import { list } from './config/data.js'
  34 + import api from '@/api'
  35 +
  36 + export default {
  37 + mixins: [MescrollCompMixin],
  38 + components: {
  39 + fTabbar,
  40 + basicInfo,
  41 + realtimeData,
  42 + alarmHistory,
  43 + historyData,
  44 + commondRecord,
  45 + },
  46 + data() {
  47 + return {
  48 + bgColor: '#fff',
  49 + activeColor: {
  50 + fontWeight: 'bold',
  51 + color: '#333',
  52 + },
  53 + inActiveColor: {
  54 + color: '#999',
  55 + },
  56 + list,
  57 + currentTab: 0,
  58 + deviceId: "",
  59 + deviceDetail: {},
  60 + keys: [],
  61 + yesterday: "",
  62 + today: "",
  63 + timeDiff: "",
  64 + historyData: [],
  65 + entityId: "",
  66 + startTs: "",
  67 + endTs: "",
  68 + recordList: [], //实时数据
  69 + isScrollable: false,
  70 + attrList: []
  71 + };
  72 + },
  73 + onUnload() {
  74 + // 页面关闭时,销毁webSocket连接,否则第二次会存在连接不到的情况
  75 + uni.closeSocket();
  76 + },
  77 + async onLoad(options) {
  78 + const {id,alarmStatus,lastOnlineTime,tbDeviceId,deviceProfileId} = options;
  79 + this.deviceId = id;
  80 + const res = await api.deviceApi.getDeviceDetail(this.deviceId)
  81 + if(!res) return
  82 + this.deviceDetail = {
  83 + ...res,
  84 + alarmStatus,
  85 + lastOnlineTime,
  86 + };
  87 + // 设备类型不是网关子设备的添加一个命令记录的选项卡
  88 + if (this.deviceDetail.deviceType !== "SENSOR") {
  89 + this.list.push({
  90 + name: "命令记录",
  91 + });
  92 + const res = new Map()
  93 + this.list = this.list.filter((item) => !res.has(item.name) && res.set(item.name, 1))
  94 + } else {
  95 + this.list = this.list.filter(item => item.name !=='命令记录')
  96 + }
  97 + this.isScrollable = this.list.length > 4;
  98 + if (res.deviceProfileId) {
  99 + const getAttrList = await api.deviceApi.getAttribute(res.deviceProfileId)
  100 + if (Array.isArray(getAttrList)) {
  101 + this.attrList = getAttrList.map(m => {
  102 + return m.identifier
  103 + })
  104 + }
  105 + }
  106 + // 连接webSockte
  107 + const socketTask = uni.connectSocket({
  108 + url: `${base.socketPrefix}://${base.baseWebSocketUrl}/api/ws/plugins/telemetry?token=` + uni.getStorageSync("userInfo").isToken, //仅为示例,并非真实接口地址。
  109 + complete: () => {},
  110 + });
  111 + uni.onSocketOpen((header) => {
  112 + socketTask.send({
  113 + data: JSON.stringify({
  114 + attrSubCmds: [],
  115 + tsSubCmds: [{
  116 + entityType: "DEVICE",
  117 + entityId: tbDeviceId,
  118 + scope: "LATEST_TELEMETRY",
  119 + cmdId: 1,
  120 + keys: this.attrList.join(','),
  121 + }, ],
  122 + historyCmds: [],
  123 + entityDataCmds: [],
  124 + entityDataUnsubscribeCmds: [],
  125 + alarmDataCmds: [],
  126 + alarmDataUnsubscribeCmds: [],
  127 + entityCountCmds: [],
  128 + entityCountUnsubscribeCmds: [],
  129 + }),
  130 + success() {},
  131 + });
  132 + });
  133 + socketTask.onMessage((msg) => {
  134 + const { data } = JSON.parse(msg.data);
  135 + const newArray = [];
  136 + for (const key in data) {
  137 + const [time, value] = data[key].flat(1);
  138 + let obj = { key,time,value, };
  139 + if (this.recordList.length === 0) {
  140 + this.recordList.unshift(obj);
  141 + } else {
  142 + newArray.push(obj);
  143 + }
  144 + }
  145 + newArray.forEach((item) => {
  146 + let flag = false;
  147 + this.recordList.forEach((item1) => {
  148 + if (item1.key === item.key) {
  149 + item1.value = item.value;
  150 + item1.time = item.time;
  151 + flag = true;
  152 + }
  153 + });
  154 + if (!flag) {
  155 + this.recordList.unshift(item);
  156 + }
  157 + });
  158 + this.recordList = this.recordList.map((item) => {
  159 + return {
  160 + ...item,
  161 + time: formatToDate(item.time, "YYYY-MM-DD HH:mm:ss"),
  162 + };
  163 + });
  164 + });
  165 + const keys = await getDeviceKeys(tbDeviceId);
  166 + this.keys = [keys];
  167 + // 昨天
  168 + this.yesterday = moment().subtract(1, "days").format("YYYY-MM-DD");
  169 + // 今天
  170 + this.today = moment().format("YYYY-MM-DD");
  171 + // 开始时间
  172 + this.startTs = moment().subtract(1, "days").format("x");
  173 + // 结束时间
  174 + this.endTs = moment().format("x");
  175 + this.entityId = tbDeviceId;
  176 + const data = await getHistoryData({
  177 + entityId: tbDeviceId,
  178 + startTs: this.startTs,
  179 + endTs: this.endTs,
  180 + keys: keys[0],
  181 + // interval: 1800000,
  182 + limit: 7,
  183 + agg: 'NONE'
  184 + });
  185 + this.timeDiff = "30分钟";
  186 + if (!Object.keys(data).length) return;
  187 + this.historyData = data[keys[0]].map((item) => {
  188 + return {
  189 + value: item.value,
  190 + ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
  191 + };
  192 + });
  193 + },
  194 + methods: {
  195 + handleTabClick({
  196 + index
  197 + }) {
  198 + this.currentTab = index;
  199 + },
  200 + handleUpdate(data, e) {
  201 + if (!Array.isArray(data)) {
  202 + this.historyData = [];
  203 + return;
  204 + }
  205 + this.historyData = data.map((item) => {
  206 + return {
  207 + value: item.value,
  208 + ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
  209 + };
  210 + });
  211 + },
  212 + },
  213 + };
  214 +</script>
  215 +
  216 +<style lang="scss" scoped>
  217 + .device-detail-page {
  218 + height: 100vh;
  219 + background-color: #f8f9fa;
  220 + }
  221 +</style>
\ No newline at end of file
... ...
device-subpackage/device-detail/device-position.vue renamed from deviceSubPage/deviceDetailPage/devicePosition.vue
  1 +.command-detail {
  2 + padding: 5rpx 30rpx;
  3 + height: 100vh;
  4 + background-color: #f8f9fa;
  5 +
  6 + .detail-top {
  7 + height: 118rpx;
  8 + width: 690rpx;
  9 + display: flex;
  10 + align-items: center;
  11 + background-color: #fff;
  12 + color: #333;
  13 + border-radius: 20rpx;
  14 + font-size: 15px;
  15 + margin-top: 30rpx;
  16 + padding: 30rpx;
  17 + }
  18 +
  19 + .detail {
  20 + background-color: #fff;
  21 + margin-top: 30rpx;
  22 + border-radius: 20rpx;
  23 + width: 690rpx;
  24 +
  25 + .detail-item {
  26 + padding: 30rpx;
  27 + display: flex;
  28 + align-items: center;
  29 +
  30 + .detail-label {
  31 + color: #333;
  32 + font-size: 15px;
  33 + }
  34 +
  35 + .detail-value {
  36 + color: #666;
  37 + font-size: 14px;
  38 + margin-left: 30rpx;
  39 + }
  40 + }
  41 + }
  42 +
  43 + .command {
  44 + margin: 30rpx 0;
  45 + }
  46 + }
\ No newline at end of file
... ...
  1 +.command-record {
  2 + padding: 0 30rpx;
  3 + background: #f8f9fa;
  4 +
  5 + .filter-button {
  6 + font-size: 12px;
  7 + width: 160rpx;
  8 + height: 64rpx;
  9 + border-radius: 32rpx;
  10 + display: flex;
  11 + justify-content: center;
  12 + align-items: center;
  13 + background: #f0f1f2;
  14 + color: #666;
  15 +
  16 + image {
  17 + width: 28rpx;
  18 + height: 28rpx;
  19 + margin-left: 4rpx;
  20 + }
  21 + }
  22 + }
  23 +
  24 + .list-item {
  25 + width: 690rpx;
  26 + background-color: #fff;
  27 + border-radius: 20rpx;
  28 + margin: 20rpx auto;
  29 + color: #333;
  30 +
  31 + .item {
  32 + .delivered-color {
  33 + color: blue;
  34 + }
  35 +
  36 + padding: 30rpx;
  37 +
  38 + view {
  39 + font-size: 14px;
  40 + margin-bottom: 10rpx;
  41 + }
  42 +
  43 + .time {
  44 + margin-top: 20rpx;
  45 + color: #999;
  46 + }
  47 +
  48 + .item-first {
  49 + display: flex;
  50 + justify-content: space-between;
  51 + align-items: center;
  52 + font-size: 15px;
  53 + font-weight: 500;
  54 + align-items: center;
  55 +
  56 + .item-right {
  57 + display: flex;
  58 + justify-content: center;
  59 + align-items: center;
  60 + width: 104rpx;
  61 + height: 36rpx;
  62 + font-size: 10px;
  63 + border-radius: 20rpx;
  64 + }
  65 +
  66 + .item-fail {
  67 + color: #848383;
  68 + background-color: #84838325;
  69 + }
  70 +
  71 + .item.success {
  72 + color: #00c9a7;
  73 + background-color: #00c9a725;
  74 + }
  75 + }
  76 + }
  77 + }
  78 +
  79 + .filter {
  80 + padding: 0 30rpx;
  81 +
  82 + .filter-title {
  83 + text-align: center;
  84 + margin-top: 14px;
  85 + font-size: 16px;
  86 + font-weight: 700;
  87 + }
  88 +
  89 + .button-group {
  90 + display: flex;
  91 + margin-top: 40rpx;
  92 + justify-content: space-between;
  93 +
  94 + view {
  95 + width: 330rpx;
  96 + }
  97 + }
  98 +
  99 + .command-time-text {
  100 + color: #333;
  101 + font-size: 14px;
  102 + font-weight: 700;
  103 + }
  104 + }
\ No newline at end of file
... ...
device-subpackage/device-detail/static/modal.css renamed from deviceSubPage/deviceDetailPage/styles/modal.css
  1 +/* ==================
  2 +模态窗口 采用colorsui的部分样式
  3 +==================== */
  4 +
1 5 .cu-modal {
2 6 position: fixed;
3 7 top: 0;
... ... @@ -18,7 +22,7 @@
18 22 }
19 23
20 24 .cu-modal::before {
21   - content: "\200B";
  25 + content: '\200B';
22 26 display: inline-block;
23 27 height: 100%;
24 28 vertical-align: middle;
... ... @@ -89,8 +93,8 @@
89 93 transform: translateX(0%);
90 94 }
91 95
92   -.cu-modal .cu-dialog>.cu-bar:first-child .action {
  96 +.cu-modal .cu-dialog > .cu-bar:first-child .action {
93 97 min-width: 100rpx;
94 98 margin-right: 0;
95 99 min-height: 100rpx;
96   -}
  100 +}
\ No newline at end of file
... ...
device-subpackage/device-detail/static/realtime-data.scss renamed from deviceSubPage/deviceDetailPage/tabDetail/realtimeData.vue
1   -<template>
2   - <view class="realtime-page">
3   - <view class="item" v-for="(item, index) in recordList" :key="index">
4   - <view class="item-top">
5   - <view>{{ item.key }}</view>
6   - <view class="item-value">{{ item.value }}</view>
7   - </view>
8   - <view class="item-time">{{ item.time }}</view>
9   - </view>
10   - <mescroll-empty v-if="!recordList.length" />
11   - </view>
12   -</template>
13   -
14   -<script>
15   -export default {
16   - props: {
17   - recordList: {
18   - type: Array,
19   - default: () => []
20   - }
21   - }
22   -};
23   -</script>
24   -
25   -<style lang="scss" scoped>
26   -.realtime-page {
27   - .item {
28   - margin: 30rpx;
29   - padding: 30rpx;
30   - border-radius: 20rpx;
31   - background-color: #fff;
32   - height: 160rpx;
33   - width: 690rpx;
34   - .item-top {
35   - display: flex;
36   - justify-content: space-between;
37   - color: #333;
38   - font-size: 16px;
39   - font-family: PingFangSC-Medium, PingFang SC;
40   - font-weight: bold;
41   - .item-value {
42   - font-weight: bold;
43   - }
44   - }
45   - .item-time {
46   - margin-top: 4rpx;
47   - font-size: 13px;
48   - color: #999;
49   - }
50   - }
51   -}
52   -</style>
  1 +.realtime-page {
  2 + .item {
  3 + margin: 30rpx;
  4 + padding: 30rpx;
  5 + border-radius: 20rpx;
  6 + background-color: #fff;
  7 + height: 160rpx;
  8 + width: 690rpx;
  9 + .item-top {
  10 + display: flex;
  11 + justify-content: space-between;
  12 + color: #333;
  13 + font-size: 16px;
  14 + font-family: PingFangSC-Medium, PingFang SC;
  15 + font-weight: bold;
  16 + .item-value {
  17 + font-weight: bold;
  18 + }
  19 + }
  20 + .item-time {
  21 + margin-top: 4rpx;
  22 + font-size: 13px;
  23 + color: #999;
  24 + }
  25 + }
  26 +}
\ No newline at end of file
... ...
1   -<template>
2   - <!-- 单向没有响应失败状态 -->
3   - <!-- 响应类型 -->
4   - <view class="command-record">
5   - <view class="filter-button" @click="openSearchDialog">
6   - <text>筛选</text>
7   - <image src="../../../static/shaixuan.png" />
8   - </view>
9   -
10   - <mescroll-uni ref="mescrollRef" @init="mescrollInit" :down="downOption" @down="downCallback" @up="upCallback"
11   - height="700px">
12   - <view @click="openCommandDetail(item)" class="list-item" v-for="(item, index) in list" :key="index">
13   - <view class="item">
14   - <view class="item-first">
15   - <text>{{ item.deviceName }}</text>
16   - <view v-if="!item.request.oneway">
17   - <view class="item-right item-success" v-if="item.response">响应成功</view>
18   - <view class="item-right item-fail" v-else>响应失败</view>
19   - </view>
20   - </view>
21   - <view>
22   - 命令类型:
23   - <text style="margin-left: 16rpx;">{{ item.additionalInfo.cmdType===1?'服务':'自定义' }}</text>
24   - </view>
25   - <view v-if="item.statusName">
26   - 命令状态:
27   - <text :style="{
28   - color:
29   - item.status == 'EXPIRED'
30   - ? 'red'
31   - : item.status == 'DELIVERED'
32   - ? 'blue'
33   - : item.status == 'QUEUED'
34   - ? '#00C9A7'
35   - : item.status == 'TIMEOUT'
36   - ? 'red'
37   - : item.status == 'SENT'
38   - ? '#00C9A7'
39   - : ''
40   - }" style="margin-left: 16rpx;">
41   - {{ item.statusName }}
42   - </text>
43   - </view>
44   - <view class="item-first">
45   - <view>
46   - 响应类型:
47   - <text style="margin-left: 16rpx;">{{ !item.request.oneway?'双向':'单向' }}</text>
48   - </view>
49   - <view class="time">{{ format(item.createTime) }}</view>
50   - </view>
51   - </view>
52   - </view>
53   - </mescroll-uni>
54   - <!-- 告警筛选 -->
55   - <u-popup @close="close" closeable bgColor="#fff" :show="show" mode="bottom" :round="20"
56   - @touchmove.stop.prevent="disabledScroll">
57   - <view class="filter" @touchmove.stop.prevent="disabledScroll">
58   - <view class="filter-title"><text>筛选条件</text></view>
59   - <FilterItem :filterList="issueStatus" title="下发状态"
60   - @clickTag="currentIndex => handleClickTag(currentIndex, issueStatus)"></FilterItem>
61   - <view class="button-group">
62   - <view>
63   - <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="重置"
64   - @click="resetFilter"></u-button>
65   - </view>
66   - <view>
67   - <u-button color="#3388ff" shape="circle" text="确认" @click="confirmFilter"></u-button>
68   - </view>
69   - </view>
70   - </view>
71   - </u-popup>
72   - <u-calendar :show="showCalendar" mode="range" @confirm="calendarConfirm" @close="calendarClose" startText="开始时间"
73   - endText="结束时间" confirmDisabledText="请选择日期" :formatter="formatter"></u-calendar>
74   - </view>
75   -</template>
76   -<script>
77   - import FilterItem from '@/pages/device/FilterItem.vue';
78   - import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
79   - import {
80   - formatToDate
81   - } from '@/plugins/utils.js';
82   - import {
83   - debounce
84   - } from '@/plugins/throttle.js';
85   - export default {
86   - mixins: [MescrollMixin],
87   - components: {
88   - FilterItem
89   - },
90   - props: {
91   - tbDeviceId: {
92   - type: String,
93   - default: ''
94   - }
95   - },
96   - data() {
97   - return {
98   - show: false,
99   - list: [],
100   - total: '',
101   - timeData: {
102   - selectTime: '',
103   - getTimeGap: ''
104   - },
105   - showCalendar: false,
106   - issueStatus: [{
107   - checked: true,
108   - name: '全部',
109   - type: ''
110   - },
111   - {
112   - checked: false,
113   - name: '响应成功',
114   - type: 'SUCCESSFUL'
115   - },
116   - {
117   - checked: false,
118   - name: '发送成功',
119   - type: 'DELIVERED'
120   - },
121   - {
122   - checked: false,
123   - name: '已过期',
124   - type: 'EXPIRED'
125   - },
126   - {
127   - checked: false,
128   - name: '响应失败',
129   - type: 'FAILED'
130   - }
131   - ],
132   - downOption: {
133   - auto: false //是否在初始化后,自动执行downCallback; 默认true
134   - },
135   - page: {
136   - num: 0,
137   - size: 10
138   - }
139   - };
140   - },
141   - methods: {
142   - /*下拉刷新的回调 */
143   - downCallback() {
144   - //联网加载数据
145   - this.list = [];
146   - this.page.num = 1;
147   - this.loadData(this.page.num, {
148   - tbDeviceId: this.tbDeviceId
149   - });
150   - },
151   - format(date) {
152   - return formatToDate(date, 'YYYY-MM-DD HH:mm:ss');
153   - },
154   - disabledScroll() {
155   - return;
156   - },
157   - /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
158   - upCallback() {
159   - //联网加载数据
160   - this.page.num += 1;
161   - this.loadData(this.page.num, {
162   - tbDeviceId: this.tbDeviceId
163   - });
164   - },
165   - //获取告警数据
166   - loadData(pageNo, params = {}) {
167   - let httpData = {
168   - ...params,
169   - page: pageNo,
170   - pageSize: 10
171   - };
172   - uni.$u.http
173   - .get('/yt/rpc', {
174   - params: httpData,
175   - custom: {
176   - load: false
177   - }
178   - })
179   - .then(res => {
180   - this.total = res.total;
181   - uni.stopPullDownRefresh();
182   - //方法一(推荐): 后台接口有返回列表的总页数 totalPage
183   - this.mescroll.endByPage(res.items.length, res.total); //必传参数(当前页的数据个数, 总页数)
184   - if (pageNo == 1) {
185   - this.list = res.items;
186   - } else {
187   - this.list = this.list.concat(res.items);
188   - }
189   - })
190   - .catch(() => {
191   - //联网失败, 结束加载
192   - this.mescroll.endErr();
193   - });
194   - },
195   - handleClickTag(currentIndex, list) {
196   - list.map((item, index) => {
197   - item.checked = index === currentIndex;
198   - });
199   - },
200   - resetFilter() {
201   - const {
202   - issueStatus
203   - } = this;
204   - issueStatus.forEach(item => item.map((item, index) => (item.checked = index === 0)));
205   - },
206   - close() {
207   - this.show = false;
208   - },
209   - openSearchDialog() {
210   - this.show = true;
211   - },
212   - hideKeyboard() {
213   - uni.hideKeyboard();
214   - },
215   - calendarConfirm(e) {
216   - this.showCalendar = false;
217   - this.timeData.selectTime = `${e[0]} / ${e[e.length - 1]}`;
218   - },
219   - confirmFilter() {
220   - const issueStatus = this.issueStatus.find(item => item.checked);
221   - this.loadData(1, {
222   - status: issueStatus.type ? issueStatus.type : undefined,
223   - tbDeviceId: this.tbDeviceId
224   - });
225   - this.show = false;
226   - },
227   - calendarClose() {
228   - this.showCalendar = false;
229   - },
230   - openCommandDetail(item) {
231   - uni.navigateTo({
232   - url: '/deviceSubPage/deviceDetailPage/tabDetail/CommandDetail?data=' + JSON.stringify(item)
233   - });
234   - }
235   - }
236   - };
237   -</script>
238   -
239   -<style lang="scss" scoped>
240   - .command-record {
241   - padding: 0 30rpx;
242   - background: #f8f9fa;
243   -
244   - .filter-button {
245   - font-size: 12px;
246   - width: 160rpx;
247   - height: 64rpx;
248   - border-radius: 32rpx;
249   - display: flex;
250   - justify-content: center;
251   - align-items: center;
252   - background: #f0f1f2;
253   - color: #666;
254   -
255   - image {
256   - width: 28rpx;
257   - height: 28rpx;
258   - margin-left: 4rpx;
259   - }
260   - }
261   - }
262   -
263   - .list-item {
264   - width: 690rpx;
265   - background-color: #fff;
266   - border-radius: 20rpx;
267   - margin: 20rpx auto;
268   - color: #333;
269   -
270   - .item {
271   - .delivered-color {
272   - color: blue;
273   - }
274   -
275   - padding: 30rpx;
276   -
277   - view {
278   - font-size: 14px;
279   - margin-bottom: 10rpx;
280   - }
281   -
282   - .time {
283   - margin-top: 20rpx;
284   - color: #999;
285   - }
286   -
287   - .item-first {
288   - display: flex;
289   - justify-content: space-between;
290   - align-items: center;
291   - font-size: 15px;
292   - font-weight: 500;
293   - align-items: center;
294   -
295   - .item-right {
296   - display: flex;
297   - justify-content: center;
298   - align-items: center;
299   - width: 104rpx;
300   - height: 36rpx;
301   - font-size: 10px;
302   - border-radius: 20rpx;
303   - }
304   -
305   - .item-fail {
306   - color: #848383;
307   - background-color: #84838325;
308   - }
309   -
310   - .item.success {
311   - color: #00c9a7;
312   - background-color: #00c9a725;
313   - }
314   - }
315   - }
316   - }
317   -
318   - .filter {
319   - padding: 0 30rpx;
320   -
321   - .filter-title {
322   - text-align: center;
323   - margin-top: 14px;
324   - font-size: 16px;
325   - font-weight: 700;
326   - }
327   -
328   - .button-group {
329   - display: flex;
330   - margin-top: 40rpx;
331   - justify-content: space-between;
332   -
333   - view {
334   - width: 330rpx;
335   - }
336   - }
337   - }
338   -</style>