Commit 6924fd95f8816be48382b61259a8f57c29b1a6d0

Authored by xp.Huang
2 parents 981e6ea0 7e746ed4

Merge branch 'main_dev' into 'main'

Main dev

See merge request yunteng/thingskit-app!153
1 1 //获取首页统计数据API
2 2 const getHomeStatisticsApi = () => {
3   - return uni.$u.http.get("/yt/homepage/app?login=false")
4   -};
  3 + return uni.$u.http.get('/yt/homepage/app?login=false')
  4 +}
5 5
6 6 //获取视频分页API
7 7 const getCameraApi = (params = {}) => {
8   - return uni.$u.http
9   - .get('/yt/video', params)
10   -};
  8 + return uni.$u.http.get('/yt/video', params)
  9 +}
11 10
12 11 //获取视频详情API
13 12 const byCameraIdGetDetailApi = (id) => {
14   - return uni.$u.http.get(`/yt/video/url/${id}`)
15   -};
  13 + return uni.$u.http.get(`/yt/video/url/${id}`)
  14 +}
  15 +
  16 +const getCameraGBTUrl = (deviceId, channelId) => {
  17 + return uni.$u.http.get(`/yt/video/control/start/${deviceId}/${channelId}`)
  18 +}
16 19
17 20 //获取组态分页API
18 21 const getConfigurationApi = (params = {}) => {
19   - return uni.$u.http
20   - .get('/yt/configuration/center', params)
  22 + return uni.$u.http.get('/yt/configuration/center', params)
21 23 }
22 24
23 25 // 获取看板分页API
24 26 const getVisualBoardApi = (params) => {
25   - return uni.$u.http.get('yt/data_board',params)
  27 + return uni.$u.http.get('yt/data_board', params)
26 28 }
27 29
28 30 //获取组织列表
29 31 const getMeOrgListApi = () => {
30   - return uni.$u.http
31   - .get('/yt/organization/me/list')
  32 + return uni.$u.http.get('/yt/organization/me/list')
32 33 }
33 34
34 35 export default {
35   - getHomeStatisticsApi,
36   - getCameraApi,
37   - byCameraIdGetDetailApi,
38   - getConfigurationApi,
39   - getMeOrgListApi,
40   - getVisualBoardApi
  36 + getHomeStatisticsApi,
  37 + getCameraApi,
  38 + byCameraIdGetDetailApi,
  39 + getConfigurationApi,
  40 + getMeOrgListApi,
  41 + getVisualBoardApi,
  42 + getCameraGBTUrl,
41 43 }
... ...
... ... @@ -17,7 +17,7 @@
17 17 </view>
18 18 </view>
19 19 <!-- 命令下发 设备在线并且不是网关子设备 -->
20   - <view class="mr-2" v-if="deviceDetail.deviceState === 'ONLINE' && deviceDetail.deviceType !== 'SENSOR'">
  20 + <view class="mr-2" v-if="deviceDetail.deviceState === 'ONLINE' && deviceDetail.transportType!==deviceTypeNum.GBT">
21 21 <!-- #ifdef MP -->
22 22 <u-button type="primary" shape="circle" size="mini" text="下发命令" @click="handleMpShowModal" />
23 23 <!-- #endif -->
... ... @@ -112,11 +112,11 @@
112 112
113 113 <script>
114 114 import {formatToDate} from '@/plugins/utils.js';
115   - import {issueCommand} from '../api/index.js';
116 115 import api from '@/api/index.js';
117 116 import mpCommandIssuance from './mp-command-issuance.vue';
118 117 import {commandTypeList} from '../config/data.js'
119 118 import {useShowModal} from '@/plugins/utils.js'
  119 + import {deviceTypeNum} from '../config/data'
120 120
121 121 export default {
122 122 components: {
... ... @@ -133,6 +133,7 @@
133 133 showNativeModal: false,
134 134 current: 0,
135 135 commandTypeList,
  136 + deviceTypeNum:deviceTypeNum,
136 137 commandTypeStr: 'OneWay',
137 138 inputCommandContent: '',
138 139 mpShowModal: false,
... ... @@ -414,4 +415,4 @@
414 415 /deep/ .u-modal__content {
415 416 padding: 30rpx 0 !important;
416 417 }
417   -</style>
\ No newline at end of file
  418 +</style>
... ...
... ... @@ -62,7 +62,7 @@
62 62 @close="calendarClose"></u-calendar>
63 63 <u-picker :show="showTimeGap" :columns="columns" keyName="label" closeOnClickOverlay @confirm="confirmTimeGap"
64 64 @cancel="cancelTimeGap" @close="cancelTimeGap" :defaultIndex="[3]"></u-picker>
65   - <u-picker :show="showSelectType" :columns="keys" closeOnClickOverlay @confirm="confirmTypeGap"
  65 + <u-picker :show="showSelectType" :columns="[keys.map(item=>({label:item.name,value:item.identifier}))]" keyName="label" closeOnClickOverlay @confirm="confirmTypeGap"
66 66 @cancel="cancelTypeGap" @close="cancelTypeGap"></u-picker>
67 67 <u-picker :show="showSelectAvg" :columns="avgColumns" keyName="label" closeOnClickOverlay
68 68 @confirm="confirmAvgGap" @cancel="showSelectAvg=false" @close="showSelectAvg=false"></u-picker>
... ... @@ -163,7 +163,7 @@
163 163 chartData: {
164 164 categories: this.historyData.length && this.historyData.map(item => item.ts),
165 165 series: [{
166   - name: this.keys[0][0],
  166 + name: this.keys[0].identifier,
167 167 data: this.historyData.length && this.historyData.map(item => Number(item.value))
168 168 }]
169 169 },
... ... @@ -199,7 +199,7 @@
199 199 timeData: {
200 200 selectTime: this.yesterday + ' 至 ' + this.today,
201 201 getTimeGap: this.timeDiff,
202   - getType: this.keys[0][0],
  202 + getType: this.keys[0].name,
203 203 limit: 7,
204 204 agg: 'NONE'
205 205 },
... ... @@ -214,7 +214,7 @@
214 214 } else {
215 215 this.chartData.categories = newValue.map(item => item.ts);
216 216 this.chartData.series = [{
217   - name: this.keys[0][0],
  217 + name: this.keys[0].name,
218 218 data: newValue.map(item => Number(item.value))
219 219 }];
220 220 }
... ... @@ -363,18 +363,18 @@
363 363 },
364 364 async confirmTypeGap(time) {
365 365 this.showSelectType = false;
366   - this.timeData.getType = time.value[0];
  366 + this.timeData.getType = time.value[0].label;
367 367 const interval = this.columns[0].find(item => item.label === this.timeData.getTimeGap);
368 368 const data = await getHistoryData({
369 369 startTs: this.startTs,
370 370 endTs: this.endTs,
371   - keys: this.timeData.getType,
  371 + keys:time.value[0].value,
372 372 interval: this.limitFlag ? null : interval.value,
373 373 entityId: this.entityId,
374 374 limit: this.timeData.limit,
375 375 agg: this.timeData.agg
376 376 });
377   - this.$emit('update', data[this.timeData.getType]);
  377 + this.$emit('update', data[time.value[0].value]);
378 378 },
379 379 cancelTypeGap() {
380 380 this.showSelectType = false;
... ...
... ... @@ -2,8 +2,8 @@
2 2 <view class="realtime-page">
3 3 <view class="item" v-for="(item, index) in recordList" :key="index">
4 4 <view class="item-top">
5   - <view>{{ item.key }}</view>
6   - <view class="item-value">{{ item.value }}</view>
  5 + <view>{{ item.name }}</view>
  6 + <view class="item-value">{{ item.value || '' }}</view>
7 7 </view>
8 8 <view class="item-time">{{ item.time }}</view>
9 9 </view>
... ... @@ -18,10 +18,10 @@
18 18 type: Array,
19 19 default: () => []
20 20 }
21   - }
  21 + },
22 22 };
23 23 </script>
24 24
25 25 <style lang="scss" scoped>
26 26 @import "../static/realtime-data.scss";
27   -</style>
\ No newline at end of file
  27 +</style>
... ...
1   -const list = [{
2   - name: "基础信息",
3   - },
4   - {
5   - name: "实时数据",
6   - },
7   - {
8   - name: "历史数据",
9   - },
10   - {
11   - name: "告警记录",
12   - },
  1 +const list = [
  2 + {
  3 + name: '基础信息',
  4 + },
  5 + {
  6 + name: '实时数据',
  7 + },
  8 + {
  9 + name: '历史数据',
  10 + },
  11 + {
  12 + name: '告警记录',
  13 + },
13 14 ]
14 15
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   - }
  16 +const issueStatus = [
  17 + {
  18 + checked: true,
  19 + name: '全部',
  20 + type: '',
  21 + },
  22 + {
  23 + checked: false,
  24 + name: '队列中',
  25 + type: 'QUEUED',
  26 + },
  27 + {
  28 + checked: false,
  29 + name: '已发送',
  30 + type: 'SENT',
  31 + },
  32 + {
  33 + checked: false,
  34 + name: '发送成功',
  35 + type: 'DELIVERED',
  36 + },
  37 + {
  38 + checked: false,
  39 + name: '响应成功',
  40 + type: 'SUCCESSFUL',
  41 + },
  42 + {
  43 + checked: false,
  44 + name: '超时',
  45 + type: 'TIMEOUT',
  46 + },
  47 + {
  48 + checked: false,
  49 + name: '已过期',
  50 + type: 'EXPIRED',
  51 + },
  52 + {
  53 + checked: false,
  54 + name: '响应失败',
  55 + type: 'FAILED',
  56 + },
  57 + {
  58 + checked: false,
  59 + name: '已删除',
  60 + type: 'DELETED',
  61 + },
60 62 ]
61 63
62   -const commandTypeList = [{
63   - value: 'OneWay',
64   - name: '单向',
65   - },
66   - {
67   - value: 'TwoWay',
68   - name: '双向'
69   - },
  64 +const commandTypeList = [
  65 + {
  66 + value: 'OneWay',
  67 + name: '单向',
  68 + },
  69 + {
  70 + value: 'TwoWay',
  71 + name: '双向',
  72 + },
70 73 ]
71 74
  75 +const deviceTypeNum = {
  76 + GBT: 'GBT28181',
  77 +}
72 78
73   -export {
74   - list,
75   - issueStatus,
76   - commandTypeList
77   -}
\ No newline at end of file
  79 +export { list, issueStatus, commandTypeList, deviceTypeNum }
... ...
... ... @@ -3,220 +3,236 @@
3 3 <!-- 公共组件-每个页面必须引入 -->
4 4 <public-module></public-module>
5 5 <u-sticky :bgColor="bgColor">
6   - <u-tabs :list="list" :current="currentTab" @click="handleTabClick" :activeStyle="activeColor"
7   - :inactiveStyle="inActiveColor" :scrollable="isScrollable" />
  6 + <u-tabs :list="list" :current="currentTab" :lineWidth="transportType == deviceTypeNum.GBT?0:30" @click=" handleTabClick " :activeStyle="{activeColor}"
  7 + :inactiveStyle="inActiveColor" :scrollable="isScrollable" itemStyle="padding: 0 11px;display:flex;flex-direction:row;align-items:center;justify-content:start;height:44px" />
8 8 </u-sticky>
9 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" />
  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 " :timeDiff=" timeDiff "
  13 + :historyData=" historyData " :entityId=" entityId " :start=" startTs " :end=" endTs " @update=" handleUpdate " />
  14 + <alarm-history v-show=" currentTab === 3 " :deviceId=" deviceId " />
  15 + <commond-record v-if=" currentTab === 4 " :tbDeviceId=" entityId " />
17 16 </view>
18 17 </view>
19 18 </template>
20 19
21 20 <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'
  21 +import fTabbar from "@/components/module/f-tabbar/f-tabbar";
  22 +import basicInfo from "./components/basic-info.vue";
  23 +import realtimeData from "./components/realtime-data.vue";
  24 +import alarmHistory from "./components/alarm-history.vue";
  25 +import historyData from "./components/history-data.vue";
  26 +import commondRecord from "./components/command-record.vue";
  27 +import { getDeviceKeys, getHistoryData } from "./api/index.js";
  28 +import { formatToDate } from "@/plugins/utils.js";
  29 +import MescrollCompMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mixins/mescroll-comp.js";
  30 +import moment from "moment";
  31 +import base from "@/config/baseUrl.js";
  32 +import { list } from './config/data.js'
  33 +import api from '@/api'
  34 +import { deviceTypeNum } from './config/data'
35 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   - getAttrList:[]
72   - };
73   - },
74   - onUnload() {
75   - // 页面关闭时,销毁webSocket连接,否则第二次会存在连接不到的情况
76   - uni.closeSocket();
77   - },
78   - async onLoad(options) {
79   - const {id,alarmStatus,lastOnlineTime,tbDeviceId,deviceProfileId} = options;
80   - this.deviceId = id;
81   - const res = await api.deviceApi.getDeviceDetail(this.deviceId)
82   - if(!res) return
83   - this.deviceDetail = {
84   - ...res,
85   - alarmStatus,
86   - lastOnlineTime,
87   - };
88   - // 设备类型不是网关子设备的添加一个命令记录的选项卡
89   - if (this.deviceDetail.deviceType !== "SENSOR") {
90   - this.list.push({
91   - name: "命令记录",
92   - });
93   - const res = new Map()
94   - this.list = this.list.filter((item) => !res.has(item.name) && res.set(item.name, 1))
95   - } else {
96   - this.list = this.list.filter(item => item.name !=='命令记录')
97   - }
98   - this.isScrollable = this.list.length > 4;
99   - if (res.deviceProfileId) {
100   - this.getAttrList = await api.deviceApi.getAttribute(res.deviceProfileId)
101   - if (Array.isArray(this.getAttrList)) {
102   - this.attrList = this.getAttrList.map(m => {
103   - return m.identifier
104   - })
105   - }
106   - }
107   - // 连接webSockte
108   - const socketTask = uni.connectSocket({
109   - url: `${base.socketPrefix}://${base.baseWebSocketUrl}/api/ws/plugins/telemetry?token=` + uni.getStorageSync("userInfo").isToken, //仅为示例,并非真实接口地址。
110   - complete: () => {},
  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 + getAttrList: [],
  72 + transportType:"",
  73 + deviceTypeNum,
  74 + };
  75 + },
  76 + onUnload() {
  77 + // 页面关闭时,销毁webSocket连接,否则第二次会存在连接不到的情况
  78 + uni.closeSocket();
  79 + },
  80 + async onLoad(options) {
  81 + const { id, alarmStatus, lastOnlineTime, tbDeviceId, deviceProfileId, transportType } = options;
  82 + this.deviceId = id;
  83 + this.transportType = transportType
  84 + const res = await api.deviceApi.getDeviceDetail(this.deviceId)
  85 + if (!res) return
  86 + this.deviceDetail = {
  87 + ...res,
  88 + alarmStatus,
  89 + lastOnlineTime,
  90 + transportType
  91 + };
  92 + // 设备类型不是网关子设备的添加一个命令记录的选项卡
  93 + if (this.deviceDetail.deviceType !== "SENSOR") {
  94 + this.list.push({
  95 + name: "命令记录",
111 96 });
112   - uni.onSocketOpen((header) => {
113   - socketTask.send({
114   - data: JSON.stringify({
115   - attrSubCmds: [],
116   - tsSubCmds: [{
117   - entityType: "DEVICE",
118   - entityId: tbDeviceId,
119   - scope: "LATEST_TELEMETRY",
120   - cmdId: 1,
121   - keys: this.attrList.join(','),
122   - }, ],
123   - historyCmds: [],
124   - entityDataCmds: [],
125   - entityDataUnsubscribeCmds: [],
126   - alarmDataCmds: [],
127   - alarmDataUnsubscribeCmds: [],
128   - entityCountCmds: [],
129   - entityCountUnsubscribeCmds: [],
130   - }),
131   - success() {},
132   - });
  97 + const res = new Map()
  98 + this.list = this.list.filter((item) => !res.has(item.name) && res.set(item.name, 1))
  99 + } else {
  100 + this.list = this.list.filter(item => item.name !== '命令记录')
  101 + }
  102 + if (transportType === deviceTypeNum.GBT) {
  103 + this.list = this.list.filter(item => item.name == '基础信息')
  104 + }
  105 + this.isScrollable = this.list.length > 4;
  106 + if (res.deviceProfileId) {
  107 + this.getAttrList = await api.deviceApi.getAttribute(res.deviceProfileId)
  108 + if (Array.isArray(this.getAttrList)) {
  109 + this.attrList = this.getAttrList?.map(m => {
  110 + return m.identifier
  111 + })
  112 + }
  113 + }
  114 + // 连接webSockte
  115 + const socketTask = uni.connectSocket({
  116 + url: `${base.socketPrefix}://${base.baseWebSocketUrl}/api/ws/plugins/telemetry?token=` + uni.getStorageSync("userInfo").isToken, //仅为示例,并非真实接口地址。
  117 + complete: () => { },
  118 + });
  119 + uni.onSocketOpen((header) => {
  120 + socketTask.send({
  121 + data: JSON.stringify({
  122 + attrSubCmds: [],
  123 + tsSubCmds: [{
  124 + entityType: "DEVICE",
  125 + entityId: tbDeviceId,
  126 + scope: "LATEST_TELEMETRY",
  127 + cmdId: 1,
  128 + keys: this.attrList.join(','),
  129 + },],
  130 + historyCmds: [],
  131 + entityDataCmds: [],
  132 + entityDataUnsubscribeCmds: [],
  133 + alarmDataCmds: [],
  134 + alarmDataUnsubscribeCmds: [],
  135 + entityCountCmds: [],
  136 + entityCountUnsubscribeCmds: [],
  137 + }),
  138 + success() { },
133 139 });
134   - socketTask.onMessage((msg) => {
135   - const { data } = JSON.parse(msg.data);
136   - const newArray = [];
137   - for (const key in data) {
138   - const [time, value] = data[key].flat(1);
139   - let obj = { key,time,value, };
140   - if (this.recordList.length === 0) {
141   - this.recordList.unshift(obj);
142   - } else {
143   - newArray.push(obj);
144   - }
  140 + });
  141 + socketTask.onMessage((msg) => {
  142 + const { data } = JSON.parse(msg.data);
  143 + const newArray = [];
  144 + for (const key in data) {
  145 + const [time, value] = data[key].flat(1);
  146 + let obj = { key, time, value, };
  147 + if (this.recordList.length === 0) {
  148 + this.recordList.unshift(obj);
  149 + } else {
  150 + newArray.push(obj);
145 151 }
146   - newArray.forEach((item) => {
147   - let flag = false;
148   - this.recordList.forEach((item1) => {
149   - if (item1.key === item.key) {
150   - item1.value = item.value;
151   - item1.time = item.time;
152   - flag = true;
153   - }
154   - });
155   - if (!flag) {
156   - this.recordList.unshift(item);
  152 + }
  153 + newArray.forEach((item) => {
  154 + let flag = false;
  155 + this.recordList.forEach((item1) => {
  156 + if (item1.key === item.key) {
  157 + item1.value = item.value;
  158 + item1.time = item.time;
  159 + flag = true;
157 160 }
158 161 });
159   - this.recordList = this.recordList.map((item) => {
160   - return {
161   - ...item,
162   - time: formatToDate(item.time, "YYYY-MM-DD HH:mm:ss"),
163   - };
164   - });
  162 + if (!flag) {
  163 + this.recordList.unshift(item);
  164 + }
165 165 });
166   - const keys = await getDeviceKeys(tbDeviceId);
167   - this.keys = [this.getAttrList.map(item=>item.identifier)];
168   - // 昨天
169   - this.yesterday = moment().subtract(1, "days").format("YYYY-MM-DD");
170   - // 今天
171   - this.today = moment().format("YYYY-MM-DD");
172   - // 开始时间
173   - this.startTs = moment().subtract(1, "days").format("x");
174   - // 结束时间
175   - this.endTs = moment().format("x");
176   - this.entityId = tbDeviceId;
177   - const data = await getHistoryData({
178   - entityId: tbDeviceId,
179   - startTs: this.startTs,
180   - endTs: this.endTs,
181   - keys: keys[0],
182   - // interval: 1800000,
183   - limit: 7,
184   - agg: 'NONE'
  166 + this.recordList = this.recordList?.map((item) => {
  167 + return {
  168 + ...item,
  169 + time: formatToDate(item.time, "YYYY-MM-DD HH:mm:ss"),
  170 + };
185 171 });
186   - this.timeDiff = "30分钟";
187   - if (!Object.keys(data).length) return;
188   - this.historyData = data[keys[0]].map((item) => {
  172 + if (this.getAttrList) {
  173 + this.getAttrList.forEach(item => {
  174 + this.recordList?.forEach(item1 => {
  175 + if (item.identifier === item1.key) {
  176 + item1.name = item.name
  177 + }
  178 + })
  179 + })
  180 + }
  181 + });
  182 + const keys = await getDeviceKeys(tbDeviceId);
  183 + this.keys = this.getAttrList || []
  184 + // 昨天
  185 + this.yesterday = moment().subtract(1, "days").format("YYYY-MM-DD");
  186 + // 今天
  187 + this.today = moment().format("YYYY-MM-DD");
  188 + // 开始时间
  189 + this.startTs = moment().subtract(1, "days").format("x");
  190 + // 结束时间
  191 + this.endTs = moment().format("x");
  192 + this.entityId = tbDeviceId;
  193 + const data = await getHistoryData({
  194 + entityId: tbDeviceId,
  195 + startTs: this.startTs,
  196 + endTs: this.endTs,
  197 + keys: keys[0],
  198 + // interval: 1800000,
  199 + limit: 7,
  200 + agg: 'NONE'
  201 + });
  202 + this.timeDiff = "30分钟";
  203 + if (!Object.keys(data).length) return;
  204 + this.historyData = data[keys[0]].map((item) => {
  205 + return {
  206 + value: item.value,
  207 + ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
  208 + };
  209 + });
  210 + },
  211 + methods: {
  212 + handleTabClick({
  213 + index
  214 + }) {
  215 + this.currentTab = index;
  216 + },
  217 + handleUpdate(data, e) {
  218 + if (!Array.isArray(data)) {
  219 + this.historyData = [];
  220 + return;
  221 + }
  222 + this.historyData = data.map((item) => {
189 223 return {
190 224 value: item.value,
191 225 ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
192 226 };
193 227 });
194 228 },
195   - methods: {
196   - handleTabClick({
197   - index
198   - }) {
199   - this.currentTab = index;
200   - },
201   - handleUpdate(data, e) {
202   - if (!Array.isArray(data)) {
203   - this.historyData = [];
204   - return;
205   - }
206   - this.historyData = data.map((item) => {
207   - return {
208   - value: item.value,
209   - ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
210   - };
211   - });
212   - },
213   - },
214   - };
  229 + },
  230 +};
215 231 </script>
216 232
217 233 <style lang="scss" scoped>
218   - .device-detail-page {
219   - height: 100vh;
220   - background-color: #f8f9fa;
221   - }
  234 +.device-detail-page {
  235 + height: 100vh;
  236 + background-color: #f8f9fa;
  237 +}
222 238 </style>
... ...
... ... @@ -15,6 +15,11 @@
15 15 font-weight: bold;
16 16 .item-value {
17 17 font-weight: bold;
  18 + width:200rpx;
  19 + text-align: right;
  20 + overflow: hidden;
  21 + text-overflow: ellipsis;
  22 + white-space: nowrap;
18 23 }
19 24 }
20 25 .item-time {
... ... @@ -23,4 +28,4 @@
23 28 color: #999;
24 29 }
25 30 }
26   -}
\ No newline at end of file
  31 +}
... ...
... ... @@ -67,7 +67,8 @@
67 67 async getVerifyCode() {
68 68 const phoneRegular = /^1\d{10}$/;
69 69 if (this.readonly) {
70   - useShowToast('验证码已发送~')
  70 + // useShowToast('验证码已发送~')
  71 + return
71 72 }
72 73 if (!this.loginForm.phoneNumber) {
73 74 return useShowToast('请输入手机号~')
... ... @@ -130,4 +131,4 @@
130 131
131 132 <style lang="scss" scoped>
132 133 @import './static/code.scss';
133   -</style>
\ No newline at end of file
  134 +</style>
... ...
... ... @@ -122,16 +122,16 @@
122 122 message: '请输入正确的手机号码'
123 123 }
124 124 ],
125   - email: [{
126   - required: true,
127   - message: '请输入正确的邮箱号',
128   - trigger: 'change'
129   - },
130   - {
131   - pattern: /^[0-9a-zA-Z]+@(([0-9a-zA-Z]+)[.])+[a-z]{3}$/,
132   - message: '请输入正确的邮箱号'
133   - }
134   - ]
  125 + // email: [{
  126 + // required: true,
  127 + // message: '请输入正确的邮箱号',
  128 + // trigger: 'change'
  129 + // },
  130 + // {
  131 + // pattern: /^[0-9a-zA-Z]+@(([0-9a-zA-Z]+)[.])+[a-z]{3}$/,
  132 + // message: '请输入正确的邮箱号'
  133 + // }
  134 + // ]
135 135 },
136 136 showDate: false,
137 137 dateTime: Number(new Date()),
... ...
... ... @@ -85,6 +85,7 @@ import {mapActions } from 'vuex'
85 85 },
86 86 onHide() {
87 87 this.ordId = ''
  88 + this.paramsStaus = ''
88 89 },
89 90 onLoad(e) {
90 91 if (getApp().getBindNot()) {
... ... @@ -107,9 +108,6 @@ import {mapActions } from 'vuex'
107 108 }
108 109 // 隐藏原生的tabbar
109 110 uni.hideTabBar();
110   - if (getApp().getBindNot()) {
111   - return
112   - }
113 111 },
114 112 computed: {
115 113 pageDisableScroll() {
... ...
1 1 <template>
2 2 <view class="device-list">
3   - <view @click="$emit('openDeviceDetail',item.id, item.alarmStatus, item.lastOnlineTime, item.tbDeviceId)"
  3 + <view @click="$emit('openDeviceDetail',item)"
4 4 class="list-item" v-for="item in list" :key="item.id">
5 5 <view class="u-flex item">
6 6 <view class="item-text text-clip">
... ... @@ -122,4 +122,4 @@
122 122 }
123 123 }
124 124 }
125   -</style>
\ No newline at end of file
  125 +</style>
... ...
... ... @@ -58,8 +58,8 @@
58 58 });
59 59 },
60 60 resetFilter() {
61   - const {deviceStatus,alarmStatus,typeStatus} = this;
62   - [deviceStatus, alarmStatus, typeStatus].forEach(item => item.map((item, index) => (item.checked = index ===0)));
  61 + const {deviceStatus,alarmStatus,typeStatus,collectStatus} = this;
  62 + [deviceStatus, alarmStatus, typeStatus,collectStatus].forEach(item => item.map((item, index) => (item.checked = index ===0)));
63 63 },
64 64 confirmFilter() {
65 65 const deviceState = this.deviceStatus.find(item => item.checked);
... ...
... ... @@ -64,9 +64,7 @@
64 64 if (getApp().getBindNot()) {
65 65 return
66 66 }
67   - if (!e.deviceState) {
68   - this.loadData(1);
69   - } else {
  67 + if (e.deviceState) {
70 68 let params = JSON.parse(e.deviceState);
71 69 this.conditions = {
72 70 deviceState: params
... ... @@ -87,7 +85,9 @@
87 85 this.conditions = {
88 86 organizationId: this.ordId
89 87 }
  88 + return
90 89 }
  90 + this.loadData(1);
91 91 },
92 92 methods: {
93 93 inputChanged(e) {
... ... @@ -197,9 +197,10 @@
197 197 this.resetQuery();
198 198 })
199 199 },
200   - openDeviceDetail(id, alarmStatus, lastOnlineTime, tbDeviceId) {
  200 + openDeviceDetail(values) {
  201 + const {id, alarmStatus, lastOnlineTime, tbDeviceId,transportType} = values || {}
201 202 uni.navigateTo({
202   - url: `/device-subpackage/device-detail/device-detail?id=${id}&alarmStatus=${alarmStatus}&lastOnlineTime=${lastOnlineTime}&tbDeviceId=${tbDeviceId}`
  203 + url: `/device-subpackage/device-detail/device-detail?id=${id}&alarmStatus=${alarmStatus}&lastOnlineTime=${lastOnlineTime}&tbDeviceId=${tbDeviceId}&transportType=${transportType}`
203 204 });
204 205 },
205 206 }
... ...
... ... @@ -3,30 +3,30 @@
3 3 <!-- 公共组件-每个页面必须引入 -->
4 4 <public-module></public-module>
5 5 <header-org @openOrg="openOrg" :total="cameraTotal" title="摄像头数:" :imageSrc="imageSrc"></header-org>
6   - <view style="height: 150rpx;"></view>
  6 + <view style="height: 150rpx"></view>
7 7 <!-- 自带分页组件 -->
8   - <mescroll-body height="80%" ref="mescrollRef" :up="upOption" @init="mescrollInit" :down="downOption" @down="downCallback"
9   - @up="upCallback">
  8 + <mescroll-body height="80%" ref="mescrollRef" :up="upOption" @init="mescrollInit" :down="downOption"
  9 + @down="downCallback" @up="upCallback">
10 10 <view class="camera-container">
11 11 <view class="container-item">
12 12 <!-- #ifdef MP-WEIXIN -->
13   - <view v-for="(item, index) in list" :key="item.id" class="item" >
  13 + <view v-for="(item, index) in list" :key="item.id" class="item">
14 14 <video :data-id="item.id" :data-accessMode="item.accessMode" :key="item.id" preload="none"
15 15 :id="'video' + item.id" class="video" :src="item.videoUrl" controls :title="item.name"
16   - x5-video-player-type="h5" x5-video-orientation="portraint" show-mute-btn
17   - :poster="item.avatar" @play="playVideo"></video>
18   - <view class="bottom-text text-clip w-300">
  16 + x5-video-player-type="h5" x5-video-orientation="portraint" show-mute-btn :poster="item.avatar"
  17 + @play="playVideo"></video>
  18 + <view class="bottom-text text-clip w-300">
19 19 <text class="text">{{ item.name }}</text>
20 20 </view>
21 21 </view>
22 22 <!-- #endif -->
23 23 <!-- #ifdef APP-PLUS -->
24   - <cover-view v-for="(item, index) in list" :key="item.id" class="item" style="overflow: hidden;">
  24 + <cover-view v-for="(item, index) in list" :key="item.id" class="item" style="overflow: hidden">
25 25 <video :data-id="item.id" :data-accessMode="item.accessMode" :key="item.id" preload="none"
26 26 :id="'video' + item.id" class="video" :src="item.videoUrl" controls :title="item.name"
27   - x5-video-player-type="h5" x5-video-orientation="portraint" show-mute-btn
28   - :poster="item.avatar" @play="playVideo"></video>
29   - <cover-view class="bottom-text text-clip w-300">
  27 + x5-video-player-type="h5" x5-video-orientation="portraint" show-mute-btn :poster="item.avatar"
  28 + @play="playVideo"></video>
  29 + <cover-view class="bottom-text text-clip w-300">
30 30 <cover-view class="text">{{ item.name }}</cover-view>
31 31 </cover-view>
32 32 </cover-view>
... ... @@ -36,133 +36,170 @@
36 36 <!-- <mescroll-empty v-if="!list.length" /> -->
37 37 </mescroll-body>
38 38 <!-- 自带分页组件 -->
39   - <view style="height: 60rpx;"></view>
  39 + <view style="height: 60rpx"></view>
40 40 </view>
41 41 </template>
42 42
43 43 <script>
44   - import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
45   - import api from '@/api/index.js'
46   - import {
47   - useNavigateTo
48   - } from '@/plugins/utils.js'
49   - import headerOrg from '@/components/common/header-org.vue'
  44 +import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js'
  45 +import api from '@/api/index.js'
  46 +import { useNavigateTo } from '@/plugins/utils.js'
  47 +import headerOrg from '@/components/common/header-org.vue'
  48 +import store from '@/store'
50 49
51   - export default {
52   - mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
53   - components:{
54   - headerOrg
  50 +export default {
  51 + mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
  52 + components: {
  53 + headerOrg,
  54 + },
  55 + data() {
  56 + return {
  57 + imageSrc: '/static/camer.png',
  58 + page: {
  59 + num: 0,
  60 + size: 10,
  61 + },
  62 + downOption: {
  63 + auto: true, //是否在初始化后,自动执行downCallback; 默认true
  64 + },
  65 + upOption: {
  66 + auto: false, // 不自动加载
  67 + },
  68 + current: 0,
  69 + cameraTotal: 0,
  70 + list: [],
  71 + ordId: '',
  72 + }
  73 + },
  74 + onShow() {
  75 + if (this.ordId) {
  76 + this.loadData(1, this.ordId)
  77 + }
  78 + },
  79 + onHide() {
  80 + this.ordId = ''
  81 + },
  82 + onUnload(){
  83 + store.commit('setLoadingShow', false)
  84 + },
  85 + onLoad() {
  86 + // 隐藏原生的tabbar
  87 + uni.hideTabBar()
  88 + },
  89 + methods: {
  90 + /*下拉刷新的回调 */
  91 + downCallback() {
  92 + //联网加载数据
  93 + this.page.num = 1
  94 + this.loadData(1)
55 95 },
56   - data() {
57   - return {
58   - imageSrc:'/static/camer.png',
59   - page: {
60   - num: 0,
61   - size: 10
62   - },
63   - downOption: {
64   - auto: true //是否在初始化后,自动执行downCallback; 默认true
65   - },
66   - upOption: {
67   - auto: false // 不自动加载
68   - },
69   - current: 0,
70   - cameraTotal: 0,
71   - list: [],
72   - ordId: '',
73   - };
  96 + /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
  97 + upCallback() {
  98 + //联网加载数据
  99 + this.page.num += 1
  100 + this.loadData(this.page.num)
74 101 },
75   - onShow() {
76   - if (this.ordId) {
77   - this.loadData(1, this.ordId);
  102 + async loadData(pageNo, organizationId) {
  103 + let httpData = {
  104 + page: pageNo,
  105 + pageSize: 10,
  106 + organizationId,
  107 + }
  108 + const res = await api.homeApi.getCameraApi({
  109 + params: httpData,
  110 + custom: {
  111 + load: false,
  112 + },
  113 + })
  114 + if (res) {
  115 + uni.stopPullDownRefresh()
  116 + this.mescroll.endBySize(res.items.length, res.total)
  117 + this.cameraTotal = res.total
  118 + if (!res.items.length && res.total == 0) {
  119 + this.mescroll.showEmpty()
  120 + } else {
  121 + this.mescroll.removeEmpty()
  122 + }
  123 + // this.list = res.items
  124 + this.getPlaySource(res.items)
  125 + // this.$nextTick(()=>{
  126 + // for(let item of res.items){
  127 + // if(item.accessMode===1){
  128 + // item.videoUrl = ( api.homeApi.byCameraIdGetDetailApi(item.id)).data.url
  129 + // }
  130 + // if(item.accessMode === 2){
  131 + // const {deviceId,channelNo} = item.params || {}
  132 + // try{
  133 + // const result = api.homeApi.getCameraGBTUrl(deviceId,channelNo)
  134 + // const {data:{flv}} = result || {}
  135 + // item.videoUrl = flv
  136 + // }catch(err){
  137 + // console.log(err,'error')
  138 + // }
  139 + // }
  140 + // }
  141 + // })
  142 +
  143 + if (pageNo == 1) {
  144 + this.list = res.items
  145 + } else {
  146 + this.list = this.list.concat(res.items)
  147 + }
78 148 }
79 149 },
80   - onHide() {
81   - this.ordId = '';
82   - },
83   - onLoad() {
84   - // 隐藏原生的tabbar
85   - uni.hideTabBar();
86   - },
87   - methods: {
88   - /*下拉刷新的回调 */
89   - downCallback() {
90   - //联网加载数据
91   - this.page.num = 1;
92   - this.loadData(1);
93   - },
94   - /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
95   - upCallback() {
96   - //联网加载数据
97   - this.page.num += 1;
98   - this.loadData(this.page.num);
99   - },
100   - async loadData(pageNo, organizationId) {
101   - let httpData = {
102   - page: pageNo,
103   - pageSize: 10,
104   - organizationId
105   - };
106   - const res = await api.homeApi.getCameraApi({
107   - params: httpData,
108   - custom: {
109   - load: false
110   - }
111   - })
112   - if (res) {
113   - uni.stopPullDownRefresh();
114   - this.mescroll.endBySize(res.items.length, res.total);
115   - this.cameraTotal = res.total;
116   - if(!res.items.length && res.total==0){
117   - this.mescroll.showEmpty()
118   - }else{
119   - this.mescroll.removeEmpty()
120   - }
  150 + getPlaySource(items) {
  151 + for (let item of items) {
  152 + if (item.accessMode === 1) {
  153 + api.homeApi.byCameraIdGetDetailApi(item.id).then(res => {
  154 + item.videoUrl = res.data.url
  155 + })
  156 + }
  157 + if (item.accessMode === 2) {
  158 + const { deviceId, channelNo } = item.params || {}
  159 + try {
  160 + api.homeApi.getCameraGBTUrl(deviceId, channelNo).then(res => {
  161 + const { data: { flv,fmp4,hls } } = res || {}
  162 + item.videoUrl = hls
  163 + })
121 164
122   - for(let item of res.items){
123   - if(item.accessMode===1){
124   - item.videoUrl = (await api.homeApi.byCameraIdGetDetailApi(item.id)).data.url
125   - }
126   - }
127 165
128   - if (pageNo == 1) {
129   - this.list = res.items;
130   - } else {
131   - this.list = this.list.concat(res.items);
  166 + } catch (err) {
  167 + console.log(err, 'error')
132 168 }
133 169 }
134   - },
135   - //播放视频
136   - async playVideo(e) {
137   - const {currentTarget: {dataset: {accessmode,id}} = {}} = e
138   - // let currentId = 'video' + id;
139   - // const videoContext = uni.createVideoContext(currentId, this);
140   - // videoContext.play()
141   - /**
142   - * 点击全屏播放当前视频,暂停其余视频
143   - * 兼容APP和MP端
144   - */
145   - this.videoContent = uni.createVideoContext(currentId, this);
146   - this.videoContent.requestFullScreen();
147   - // 获取视频列表
148   - let trailer = this.list;
149   - trailer.forEach((item, index) => {
150   - if (item.videoUrl != null && item.videoUrl != '') {
151   - let temp = 'video' + item.id;
152   - if (temp != currentId) {
153   - //暂停不是当前的视频
154   - uni.createVideoContext(temp, this).pause();
155   - }
156   - }
157   - });
158   - },
159   - openOrg() {
160   - useNavigateTo('/pages/organization/organization')
161 170 }
162   - }
163   - };
  171 + },
  172 + //播放视频
  173 + async playVideo(e) {
  174 + const { currentTarget: { dataset: { accessmode, id } } = {} } = e
  175 + let currentId = 'video' + id;
  176 + // const videoContext = uni.createVideoContext(currentId, this);
  177 + // videoContext.play()
  178 + /**
  179 + * 点击全屏播放当前视频,暂停其余视频
  180 + * 兼容APP和MP端
  181 + */
  182 + // this.videoContent = uni.createVideoContext(currentId, this)
  183 + // this.videoContent.requestFullScreen()
  184 + // 获取视频列表
  185 + let trailer = this.list
  186 + trailer.forEach((item, index) => {
  187 + if (item.videoUrl != null && item.videoUrl != '') {
  188 + let temp = 'video' + item.id
  189 + if (temp != currentId) {
  190 + //暂停不是当前的视频
  191 + uni.createVideoContext(temp, this).pause()
  192 + }
  193 + }
  194 + })
  195 + },
  196 + openOrg() {
  197 + useNavigateTo('/pages/organization/organization')
  198 + },
  199 + },
  200 +}
164 201 </script>
165 202
166 203 <style lang="scss" scoped>
167   - @import '../../static/camera.scss';
  204 +@import '../../static/camera.scss';
168 205 </style>
... ...
... ... @@ -82,6 +82,8 @@
82 82 onLoad() {
83 83 // 隐藏原生的tabbar
84 84 uni.hideTabBar();
  85 + },
  86 + onShow(){
85 87 if (getApp().getBindNot()) {
86 88 this.basicStatistics.map(item=>{
87 89 const { key, value } = item
... ...