Commit 84c365f3917c72548f1ee9f123e0ed7b02fb6fd2

Authored by fengwotao
1 parent 594624ad

pref: app、mp端 优化告警和设备代码

Showing 40 changed files with 3297 additions and 3482 deletions
alarm-subpackage/alarm-detail/alarm-detail.vue renamed from alarmSubPage/alarmDetailPage/alarmDetail.vue
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==='告警设备:'?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[7].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[7].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_UNACK', '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 - };  
92 - },  
93 - onLoad(e) {  
94 - if (e.data !== null) {  
95 - let params = JSON.parse(decodeURIComponent(e.data));  
96 - const {  
97 - deviceName,  
98 - severity,  
99 - organizationName,  
100 - details,  
101 - type,  
102 - createdTime,  
103 - status,  
104 - id  
105 - } = params  
106 - this.detailId = id  
107 - this.alarmDetail = [{  
108 - label: '告警场景:',  
109 - value: type  
110 - },  
111 - {  
112 - label: '告警级别:',  
113 - value: severity  
114 - },  
115 - {  
116 - label: '所属组织:',  
117 - value: organizationName  
118 - },  
119 - {  
120 - label: '告警设备:',  
121 - value: ''  
122 - },  
123 - {  
124 - label: '告警条件:',  
125 - value: ''  
126 - },  
127 - {  
128 - label: '告警值:',  
129 - value: ''  
130 - },  
131 - {  
132 - label: '告警时间:',  
133 - value: createdTime  
134 - },  
135 - {  
136 - label: '告警状态:',  
137 - value: status  
138 - },  
139 - ]  
140 - this.formatAlarmDevice(details)  
141 - this.formatAlarmValue(details)  
142 - this.formatAlarmCondition(details)  
143 - }  
144 - // 隐藏原生的tabbar  
145 - uni.hideTabBar();  
146 - },  
147 - methods: {  
148 - ...mapActions(['updateBadgeTotal']),  
149 - setAlarmStatus(value) {  
150 - return this.alarmSeverity.find(item => item.value === value).label  
151 - },  
152 - setAlarmSeverity(value) {  
153 - return this.alarmStatus.find(item => item.value === value).label  
154 - },  
155 - returnPrevPage(title) {  
156 - useShowToast(title)  
157 - let pages = getCurrentPages(); //获取所有页面栈实例列表  
158 - let nowPage = pages[pages.length - 1]; //当前页页面实例  
159 - let prevPage = pages[pages.length - 2]; //上一页页面实例  
160 - prevPage.$vm.detailStatus = true;  
161 - },  
162 - async handleSubmit() {  
163 - if (this.formModel.result == '') return uni.$u.toast('请输入处理结果');  
164 - const res = await api.alarmApi.postAlarmAckApi(this.detailId)  
165 - if (res == '') {  
166 - this.returnPrevPage('处理成功~')  
167 - setTimeout(() => {  
168 - useNavigateBack(1)  
169 - }, 500);  
170 - }  
171 - },  
172 - async handleRemove() {  
173 - const res = await api.alarmApi.postAlarmClearApi(this.detailId)  
174 - if (res == '') {  
175 - this.returnPrevPage('清除成功~')  
176 - setTimeout(async () => {  
177 - useNavigateBack(1)  
178 - const res = await uni.$u.http.get('/yt/homepage/app?login=true');  
179 - if (res) {  
180 - //异步实时更新告警徽标数  
181 - await this.updateBadgeTotal(res.totalAlarm?.activedAlarm);  
182 - }  
183 - }, 500);  
184 - }  
185 - },  
186 - async formatAlarmValue(e) {  
187 - const keys = Object.keys(e)  
188 - const dataFormat = await this.handleAlarmDetailFormat(keys);  
189 - const values = keys.reduce((acc, curr) => {  
190 - dataFormat.forEach((dataItem => {  
191 - if (dataItem.tbDeviceId === curr) {  
192 - const findAttribute = dataItem.attribute.find(findItem => findItem  
193 - .identifier === e[curr].key)  
194 - acc.push(  
195 - `${findAttribute.name}:${e[curr].realValue}${findAttribute.detail?.dataType?.specs?.unit?.key}`  
196 - )  
197 - }  
198 - }))  
199 - return acc  
200 - }, [])  
201 - this.formatAlarmValueText = values.join(',')  
202 - },  
203 - formatAlarmCondition(e) {  
204 - const keys = Object.keys(e)  
205 - const values = keys.reduce((acc, curr) => {  
206 - acc.push({  
207 - login: e[curr].logic,  
208 - logicValue: e[curr].logicValue  
209 - })  
210 - return acc  
211 - }, [])  
212 - const operation = [...operationNumberOrDate, ...operationString, ...operationBoolean]  
213 - const format = values.map(item => {  
214 - const findOperation = operation.find(findItem => findItem.value === item.login)?.symbol  
215 - return findOperation + item.logicValue  
216 - })  
217 - this.formatAlarmConditionText = format  
218 - },  
219 - async formatAlarmDevice(e) {  
220 - const keys = Object.keys(e)  
221 - const dataFormat = await this.handleAlarmDetailFormat(keys);  
222 - this.formatDeviceText = dataFormat.map(item => item.name).join(',')  
223 - },  
224 - async handleAlarmDetailFormat(keys) {  
225 - const temp = [];  
226 - for (let item of keys) {  
227 - if (item === 'key' || item === 'data') return; //旧数据则终止  
228 - const deviceDetailRes = await api.deviceApi.getDeviceDetail(item);  
229 - const {  
230 - deviceProfileId  
231 - } = deviceDetailRes;  
232 - if (!deviceProfileId) return;  
233 - const attributeRes = await api.deviceApi.getAttribute(deviceProfileId);  
234 - const dataFormat = this.handleDataFormat(deviceDetailRes, attributeRes);  
235 - temp.push(dataFormat);  
236 - }  
237 - return temp;  
238 - },  
239 - handleDataFormat(deviceDetail, attributes) {  
240 - const {  
241 - name,  
242 - tbDeviceId  
243 - } = deviceDetail;  
244 - const attribute = attributes.map((item) => ({  
245 - identifier: item.identifier,  
246 - name: item.name,  
247 - detail: item.detail  
248 - }));  
249 - return {  
250 - name,  
251 - tbDeviceId,  
252 - attribute,  
253 - };  
254 - }  
255 - }  
256 - };  
257 -</script>  
258 -  
259 -<style lang="scss" scoped>  
260 - @import './static/alarmDetail.scss';  
261 -  
262 - /deep/ .u-button--primary {  
263 - background-color: #377dff !important;  
264 - border-color: #377dff !important;  
265 - } 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==='告警设备:'?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[7].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[7].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_UNACK', '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 + };
  92 + },
  93 + onLoad(e) {
  94 + if (e.data !== null) {
  95 + let params = JSON.parse(decodeURIComponent(e.data));
  96 + const {
  97 + deviceName,
  98 + severity,
  99 + organizationName,
  100 + details,
  101 + type,
  102 + createdTime,
  103 + status,
  104 + id
  105 + } = params
  106 + this.detailId = id
  107 + this.alarmDetail = [{
  108 + label: '告警场景:',
  109 + value: type
  110 + },
  111 + {
  112 + label: '告警级别:',
  113 + value: severity
  114 + },
  115 + {
  116 + label: '所属组织:',
  117 + value: organizationName
  118 + },
  119 + {
  120 + label: '告警设备:',
  121 + value: ''
  122 + },
  123 + {
  124 + label: '告警条件:',
  125 + value: ''
  126 + },
  127 + {
  128 + label: '告警值:',
  129 + value: ''
  130 + },
  131 + {
  132 + label: '告警时间:',
  133 + value: createdTime
  134 + },
  135 + {
  136 + label: '告警状态:',
  137 + value: status
  138 + },
  139 + ]
  140 + this.formatAlarmDevice(details)
  141 + this.formatAlarmValue(details)
  142 + this.formatAlarmCondition(details)
  143 + }
  144 + // 隐藏原生的tabbar
  145 + uni.hideTabBar();
  146 + },
  147 + methods: {
  148 + ...mapActions(['updateBadgeTotal']),
  149 + setAlarmStatus(value) {
  150 + return this.alarmSeverity.find(item => item.value === value).label
  151 + },
  152 + setAlarmSeverity(value) {
  153 + return this.alarmStatus.find(item => item.value === value).label
  154 + },
  155 + returnPrevPage(title) {
  156 + useShowToast(title)
  157 + let pages = getCurrentPages(); //获取所有页面栈实例列表
  158 + let nowPage = pages[pages.length - 1]; //当前页页面实例
  159 + let prevPage = pages[pages.length - 2]; //上一页页面实例
  160 + prevPage.$vm.detailStatus = true;
  161 + },
  162 + async handleSubmit() {
  163 + if (this.formModel.result == '') return uni.$u.toast('请输入处理结果');
  164 + const res = await api.alarmApi.postAlarmAckApi(this.detailId)
  165 + if (res == '') {
  166 + this.returnPrevPage('处理成功~')
  167 + setTimeout(() => {
  168 + useNavigateBack(1)
  169 + }, 500);
  170 + }
  171 + },
  172 + async handleRemove() {
  173 + const res = await api.alarmApi.postAlarmClearApi(this.detailId)
  174 + if (res == '') {
  175 + this.returnPrevPage('清除成功~')
  176 + setTimeout(async () => {
  177 + useNavigateBack(1)
  178 + const res = await uni.$u.http.get('/yt/homepage/app?login=true');
  179 + if (res) {
  180 + //异步实时更新告警徽标数
  181 + await this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
  182 + }
  183 + }, 500);
  184 + }
  185 + },
  186 + async formatAlarmValue(e) {
  187 + const keys = Object.keys(e)
  188 + const dataFormat = await this.handleAlarmDetailFormat(keys);
  189 + const values = keys.reduce((acc, curr) => {
  190 + dataFormat.forEach((dataItem => {
  191 + if (dataItem.tbDeviceId === curr) {
  192 + const findAttribute = dataItem.attribute.find(findItem => findItem
  193 + .identifier === e[curr].key)
  194 + acc.push(
  195 + `${findAttribute.name}:${e[curr].realValue}${findAttribute.detail?.dataType?.specs?.unit?.key}`
  196 + )
  197 + }
  198 + }))
  199 + return acc
  200 + }, [])
  201 + this.formatAlarmValueText = values.join(',')
  202 + },
  203 + formatAlarmCondition(e) {
  204 + const keys = Object.keys(e)
  205 + const values = keys.reduce((acc, curr) => {
  206 + acc.push({
  207 + login: e[curr].logic,
  208 + logicValue: e[curr].logicValue
  209 + })
  210 + return acc
  211 + }, [])
  212 + const operation = [...operationNumberOrDate, ...operationString, ...operationBoolean]
  213 + const format = values.map(item => {
  214 + const findOperation = operation.find(findItem => findItem.value === item.login)?.symbol
  215 + return findOperation + item.logicValue
  216 + })
  217 + this.formatAlarmConditionText = format
  218 + },
  219 + async formatAlarmDevice(e) {
  220 + const keys = Object.keys(e)
  221 + const dataFormat = await this.handleAlarmDetailFormat(keys);
  222 + this.formatDeviceText = dataFormat.map(item => item.name).join(',')
  223 + },
  224 + async handleAlarmDetailFormat(keys) {
  225 + const temp = [];
  226 + for (let item of keys) {
  227 + if (item === 'key' || item === 'data') return; //旧数据则终止
  228 + const deviceDetailRes = await api.deviceApi.getDeviceDetail(item);
  229 + const {
  230 + deviceProfileId
  231 + } = deviceDetailRes;
  232 + if (!deviceProfileId) return;
  233 + const attributeRes = await api.deviceApi.getAttribute(deviceProfileId);
  234 + const dataFormat = this.handleDataFormat(deviceDetailRes, attributeRes);
  235 + temp.push(dataFormat);
  236 + }
  237 + return temp;
  238 + },
  239 + handleDataFormat(deviceDetail, attributes) {
  240 + const {
  241 + name,
  242 + tbDeviceId
  243 + } = deviceDetail;
  244 + const attribute = attributes.map((item) => ({
  245 + identifier: item.identifier,
  246 + name: item.name,
  247 + detail: item.detail
  248 + }));
  249 + return {
  250 + name,
  251 + tbDeviceId,
  252 + attribute,
  253 + };
  254 + }
  255 + }
  256 + };
  257 +</script>
  258 +
  259 +<style lang="scss" scoped>
  260 + @import './static/alarmDetail.scss';
  261 +
  262 + /deep/ .u-button--primary {
  263 + background-color: #377dff !important;
  264 + border-color: #377dff !important;
  265 + }
266 </style> 266 </style>
alarm-subpackage/alarm-detail/static/alarmDetail.scss renamed from alarmSubPage/alarmDetailPage/static/alarmDetail.scss
@@ -17,13 +17,24 @@ const getDeviceDetail = (id) => { @@ -17,13 +17,24 @@ const getDeviceDetail = (id) => {
17 }; 17 };
18 18
19 //设备属性 19 //设备属性
20 - const getAttribute = (deviceProfileId) => {  
21 - return uni.$u.http.get(`/yt/device/attributes/${deviceProfileId}`); 20 +const getAttribute = (deviceProfileId) => {
  21 + return uni.$u.http.get(`/yt/device/attributes/${deviceProfileId}`);
22 }; 22 };
23 23
  24 +//命令下发
  25 +const issueCommand = (type, tbDeviceId, data) => {
  26 + return uni.$u.http.post(`/rpc/${type==='OneWay'?'oneway':'twoway'}/${tbDeviceId}`, data)
  27 +}
  28 +
  29 +//获取命令下发记录
  30 +const getRpcRecord = (params) => {
  31 + return uni.$u.http.get('/yt/rpc', params);
  32 +};
24 33
25 export default { 34 export default {
26 getDeviceApi, 35 getDeviceApi,
27 getDeviceDetail, 36 getDeviceDetail,
28 - getAttribute  
29 -} 37 + getAttribute,
  38 + issueCommand,
  39 + getRpcRecord
  40 +}
@@ -103,11 +103,11 @@ uni.$u.http.interceptors.response.use( @@ -103,11 +103,11 @@ uni.$u.http.interceptors.response.use(
103 const routers = getCurrentPages(); 103 const routers = getCurrentPages();
104 const currentRoute = routers[routers.length - 1].route; 104 const currentRoute = routers[routers.length - 1].route;
105 const isLoginPage = currentRoute.includes( 105 const isLoginPage = currentRoute.includes(
106 - "publicLoginSubPage/public/login" 106 + "login-subpackage/public/login"
107 ); 107 );
108 !isLoginPage && 108 !isLoginPage &&
109 uni.reLaunch({ 109 uni.reLaunch({
110 - url: "/publicLoginSubPage/public/login", 110 + url: "/login-subpackage/public/login",
111 }); 111 });
112 // 清空登录信息 112 // 清空登录信息
113 store.commit("emptyUserInfo"); 113 store.commit("emptyUserInfo");
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="700px" 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 FilterItem from '@/pages/device/components/query-item.vue';
  21 + import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
  22 + import api from '@/api/index.js'
  23 + import alarmItem from '@/pages/alarm/components/alarm-item.vue'
  24 + import alarmPopup from '@/pages/alarm/components/alarm-popup.vue'
  25 + import {
  26 + useNavigateTo
  27 + } from '@/plugins/utils.js'
  28 +
  29 + export default {
  30 + mixins: [MescrollMixin],
  31 + components: {
  32 + FilterItem,
  33 + alarmItem,
  34 + alarmPopup
  35 + },
  36 + props: {
  37 + deviceId: {
  38 + type: String,
  39 + default: ''
  40 + }
  41 + },
  42 + data() {
  43 + return {
  44 + show: false,
  45 + list: [],
  46 + total: 0,
  47 + downOption: {
  48 + auto: true //是否在初始化后,自动执行downCallback; 默认true
  49 + },
  50 + upOption: {
  51 + auto: false // 不自动加载
  52 + },
  53 + page: {
  54 + num: 0,
  55 + size: 10
  56 + },
  57 + conditions: {},
  58 + };
  59 + },
  60 + methods: {
  61 + disabledScroll() {
  62 + return;
  63 + },
  64 + getQueryCondition(value) {
  65 + this.conditions = value
  66 + this.conditions.deviceId = this.deviceId
  67 + this.loadData(1, this.conditions);
  68 + this.close()
  69 + },
  70 + resetQuery() {
  71 + this.page.num = 1;
  72 + this.$refs.alarmPopupRef.resetQuery()
  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>
  1 +<template>
  2 +</template>
  3 +
  4 +<script>
  5 + export default {
  6 +
  7 + }
  8 +</script>
  9 +<style>
  10 +</style>
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="请输入下发内容(json格式)" v-model="inputCommandVal" />  
130 - <u-icon @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 - };  
192 - },  
193 - computed: {  
194 - deviceStatus(){  
195 - return this.deviceDetail.deviceState === 'INACTIVE' ? '待激活' : this.deviceDetail.deviceState === 'ONLINE' ? '在线' : '离线'  
196 - },  
197 - deviceType() {  
198 - return this.deviceDetail.deviceType === 'DIRECT_CONNECTION' ?  
199 - '直连设备' :  
200 - this.deviceDetail.deviceType === 'GATEWAY' ?  
201 - '网关设备' :  
202 - this.deviceDetail.deviceType === 'SENSOR' ?  
203 - '网关子设备' :  
204 - '';  
205 - },  
206 - alarmStatus() {  
207 - return this.deviceDetail.alarmStatus === '0' ? '否' : '是';  
208 - },  
209 - formatLastOnlineTime() {  
210 - return formatToDate(Number(this.deviceDetail.lastOnlineTime), 'YYYY-MM-DD HH:mm:ss');  
211 - }  
212 - },  
213 - onLoad(e) {  
214 - // 隐藏原生的tabbar  
215 - uni.hideTabBar();  
216 - },  
217 - mounted() {},  
218 - beforeCreate() {  
219 - this.modalName = null  
220 - },  
221 - methods: {  
222 - handleCopy(value) {  
223 - uni.showModal({  
224 - content: JSON.stringify(value),  
225 - confirmText: '复制内容',  
226 - showCancel:false,  
227 - success: () => {  
228 - uni.setClipboardData({  
229 - data: JSON.stringify(value),  
230 - success: () => {  
231 - uni.showToast({  
232 - title: '复制成功'  
233 - })  
234 - }  
235 - });  
236 - },  
237 -  
238 - });  
239 - },  
240 - radioChange: function(evt) {  
241 - for (let i = 0; i < this.items.length; i++) {  
242 - if (this.items[i].value === evt.detail.value) {  
243 - this.current = i;  
244 - break;  
245 - }  
246 - }  
247 - this.commandType = evt.detail.value  
248 - },  
249 - handleClick() {  
250 - const data = {  
251 - longitude: this.deviceDetail.deviceInfo.longitude || 0,  
252 - latitude: this.deviceDetail.deviceInfo.latitude || 0  
253 - };  
254 - uni.navigateTo({  
255 - url: '/deviceSubPage/deviceDetailPage/devicePosition?data=' + JSON.stringify(data)  
256 - });  
257 - },  
258 - showModal(e) {  
259 - this.modalName = e.currentTarget.dataset.target  
260 - this.showNodal = true  
261 - },  
262 - showModalBtn() {  
263 - this.showModel = true;  
264 - this.inputCommandVal = '';  
265 - },  
266 - disabledScroll() {  
267 - return;  
268 - },  
269 - hiddenModal() {  
270 - this.showModel = false;  
271 - this.inputCommandVal = '';  
272 - // #ifdef APP-PLUS  
273 - this.modalName = null  
274 - this.showNodal = false  
275 - // #endif  
276 - },  
277 - async confirmCommand() {  
278 - try {  
279 - const commandJsonValue = JSON.parse(this.inputCommandVal);  
280 - this.commandValue.persistent = true;  
281 - this.commandValue.additionalInfo = {  
282 - cmdType: 'API'  
283 - };  
284 - this.commandValue.method = 'methodThingskit';  
285 - this.commandValue.params = commandJsonValue  
286 - await issueCommand(this.commandType, this.deviceDetail.tbDeviceId, this.commandValue);  
287 - this.hiddenModal();  
288 - uni.$u.toast('下发成功~');  
289 - } catch (e) {  
290 - uni.$u.toast('下发失败~');  
291 - }  
292 - },  
293 - cancelCommand() {  
294 - this.hiddenModal();  
295 - // #ifdef APP-PLUS  
296 - this.modalName = null  
297 - this.showNodal = false  
298 - // #endif  
299 - }  
300 - }  
301 - };  
302 -</script>  
303 -  
304 -<style lang="scss" scoped>  
305 - @import url('../styles/modal.css');  
306 -  
307 - .cusAppplusContent {  
308 - width: 625rpx;  
309 - height: 400rpx;  
310 - background: #FFFFFF;  
311 - box-shadow: 2px 2px 4px 0px rgba(0, 0, 0, 0.03);  
312 - border-radius: 10px;  
313 -  
314 - }  
315 -  
316 - .cusAppplusCancelBtn {  
317 - background: #e3e3e5;  
318 - border-radius: 38rpx;  
319 - height: 85rpx;  
320 - line-height: 85rpx  
321 - }  
322 -  
323 - .cusAppplusConfrimBtn {  
324 - background: #3388ff;  
325 - border-radius: 38rpx;  
326 - height: 85rpx;  
327 - line-height: 85rpx  
328 - }  
329 -  
330 - .cu-item {  
331 - background: #3388FF;  
332 - border-radius: 12px;  
333 - width: 120rpx;  
334 - height: 48rpx;  
335 - text-align: center;  
336 - line-height: 40rpx;  
337 -  
338 - text {  
339 - font-size: 12px;  
340 - font-family: PingFangSC-Regular, PingFang SC;  
341 - font-weight: 400;  
342 - color: #FFFFFF;  
343 - }  
344 - }  
345 -  
346 -  
347 -  
348 - .basic-page {  
349 - padding: 0 30rpx;  
350 -  
351 - .basic-title {  
352 - display: flex;  
353 - justify-content: space-between;  
354 - align-items: center;  
355 - height: 140rpx;  
356 - background-color: #fff;  
357 - border-radius: 20rpx;  
358 - }  
359 -  
360 - .detail {  
361 - background-color: #fff;  
362 - margin-top: 30rpx;  
363 - border-radius: 20rpx;  
364 - width: 690rpx;  
365 -  
366 - .detail-item {  
367 - padding: 30rpx;  
368 - display: flex;  
369 - align-items: center;  
370 -  
371 - .detail-label {  
372 - color: #333;  
373 - font-size: 15px;  
374 - }  
375 -  
376 - .detail-value {  
377 - color: #666;  
378 - font-size: 14px;  
379 - margin-left: 30rpx;  
380 - }  
381 - }  
382 - }  
383 - }  
384 -  
385 - /deep/ .u-modal__content {  
386 - padding: 30rpx 0 !important;  
387 - }  
388 -  
389 - .button-group {  
390 - display: flex;  
391 - margin-top: 40rpx;  
392 - justify-content: space-between;  
393 -  
394 - view {  
395 - width: 300rpx;  
396 - }  
397 - }  
398 -</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" name="map-fill">
  10 + </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 + <u-button type="primary" shape="circle" size="mini" text="下发命令" @click="handleAppShowModal" />
  26 + <!-- #endif -->
  27 + </view>
  28 + </view>
  29 + <!-- 设备详情 -->
  30 + <view class="detail">
  31 + <view class="detail-item">
  32 + <view class="detail-label">设备编号</view>
  33 + <view class="detail-value">{{ deviceDetail.sn }}</view>
  34 + </view>
  35 + <u-line length="90%" margin="0 auto"></u-line>
  36 + <view class="detail-item">
  37 + <view class="detail-label">设备类型</view>
  38 + <view class="detail-value">{{ deviceType }}</view>
  39 + </view>
  40 + <u-line length="90%" margin="0 auto"></u-line>
  41 + <view class="detail-item">
  42 + <view class="detail-label">所属组织</view>
  43 + <view class="detail-value">{{ deviceDetail.organizationDTO.name }}</view>
  44 + </view>
  45 + <u-line length="90%" margin="0 auto"></u-line>
  46 + <view class="detail-item">
  47 + <view class="detail-label">最后连接时间</view>
  48 + <view class="detail-value">{{ formatLastOnlineTime }}</view>
  49 + </view>
  50 + <u-line length="90%" margin="0 auto"></u-line>
  51 + <view class="detail-item">
  52 + <view class="detail-label">是否告警</view>
  53 + <view class="detail-value">{{ alarmStatus }}</view>
  54 + </view>
  55 + <u-line length="90%" margin="0 auto"></u-line>
  56 + <view class="detail-item">
  57 + <view class="detail-label">设备描述</view>
  58 + <view class="detail-value">{{ deviceDetail.description }}</view>
  59 + </view>
  60 + </view>
  61 + <!-- 命令下发 -->
  62 + <!-- #ifdef APP-PLUS -->
  63 + <app-command-issuance :showModal="appShowModal"></app-command-issuance>
  64 + <!-- #endif -->
  65 + <!-- #ifdef MP -->
  66 + <!-- u-modal在app端弹窗层级无法覆盖 -->
  67 + <mp-command-issuance :isShowTCP="isShowTCP" :showModal="mpShowModal" @hideModal="hideMpModal"
  68 + @cancelCommand="cancelCommand" @confirmCommand="confirmCommand"></mp-command-issuance>
  69 + <!-- #endif -->
  70 + </view>
  71 +</template>
  72 +
  73 +<script>
  74 + import {
  75 + formatToDate
  76 + } from '@/plugins/utils.js';
  77 + import {
  78 + issueCommand
  79 + } from '../api/index.js';
  80 + import api from '@/api/index.js'
  81 + import mpCommandIssuance from './mp-command-issuance.vue'
  82 + import appCommandIssuance from './app-command-issuance.vue'
  83 + import nextModal from '@/components/module/next-modal_1.0.2/components/next-modal/next-modal.vue'
  84 +
  85 + export default {
  86 + components: {
  87 + mpCommandIssuance,
  88 + appCommandIssuance,
  89 + nextModal
  90 + },
  91 + props: {
  92 + deviceDetail: {
  93 + type: Object,
  94 + default: () => ({})
  95 + }
  96 + },
  97 + data() {
  98 + return {
  99 + mpShowModal: false,
  100 + appShowModal: false,
  101 + commandValue: {},
  102 + isShowTCP: false, //用于下发命令时判断是否是TCP/UDP
  103 + };
  104 + },
  105 + computed: {
  106 + deviceStatus() {
  107 + return this.deviceDetail.deviceState === 'INACTIVE' ? '待激活' : this.deviceDetail.deviceState === 'ONLINE' ?
  108 + '在线' : '离线'
  109 + },
  110 + deviceType() {
  111 + return this.deviceDetail.deviceType === 'DIRECT_CONNECTION' ?
  112 + '直连设备' :
  113 + this.deviceDetail.deviceType === 'GATEWAY' ?
  114 + '网关设备' :
  115 + this.deviceDetail.deviceType === 'SENSOR' ?
  116 + '网关子设备' :
  117 + '';
  118 + },
  119 + alarmStatus() {
  120 + return this.deviceDetail.alarmStatus === '0' ? '否' : '是';
  121 + },
  122 + formatLastOnlineTime() {
  123 + return formatToDate(Number(this.deviceDetail.lastOnlineTime), 'YYYY-MM-DD HH:mm:ss');
  124 + }
  125 + },
  126 + onLoad() {
  127 + // 隐藏原生的tabbar
  128 + uni.hideTabBar();
  129 + },
  130 + methods: {
  131 + formatTextStatus(deviceState) {
  132 + return deviceState === 'INACTIVE' ? '#666' : deviceState === 'ONLINE' ? '#377DFF' : '#DE4437'
  133 + },
  134 + handleClick() {
  135 + const data = {
  136 + longitude: this.deviceDetail.deviceInfo.longitude || 0,
  137 + latitude: this.deviceDetail.deviceInfo.latitude || 0
  138 + };
  139 + uni.navigateTo({
  140 + url: '/deviceSubPage/deviceDetailPage/devicePosition?data=' + JSON.stringify(data)
  141 + });
  142 + },
  143 + disabledScroll() {
  144 + return;
  145 + },
  146 + handleAppShowModal() {
  147 + this.appShowModal = true
  148 + },
  149 + handleMpShowModal() {
  150 + const {
  151 + transportType
  152 + } = this.deviceDetail.deviceProfile
  153 + this.isShowTCP = transportType == 'TCP' ? true : false
  154 + this.mpShowModal = true;
  155 + },
  156 + hideMpModal() {
  157 + this.mpShowModal = false;
  158 + },
  159 + hideAppModal() {
  160 + this.appShowModal = false
  161 + },
  162 + async confirmCommand(commandType, inputCommandVal) {
  163 + try {
  164 + if (this.isShowTCP) { //TCP的格式只能是字符串
  165 + const zg = /^[0-9a-zA-Z]*$/
  166 + if (!zg.test(inputCommandVal)) {
  167 + uni.$u.toast('输入的内容只能是字母和数字的组合')
  168 + return
  169 + }
  170 + this.commandValue.params = inputCommandVal
  171 + } else {
  172 + this.commandValue.params = JSON.parse(inputCommandVal);
  173 + }
  174 + if (!commandJsonValue) return uni.$u.toast('请输入下发内容~');
  175 + this.commandValue.persistent = true;
  176 + this.commandValue.additionalInfo = {
  177 + cmdType: 'API'
  178 + };
  179 + this.commandValue.method = 'methodThingskit';
  180 + this.commandValue.params = commandJsonValue
  181 + await api.deviceApi.issueCommand(commandType, this.deviceDetail.tbDeviceId, this.commandValue);
  182 + this.cancelCommand();
  183 + uni.$u.toast('下发成功~');
  184 + } catch (e) {
  185 + uni.$u.toast('下发失败~');
  186 + }
  187 + },
  188 + cancelCommand() {
  189 + this.hideMpModal();
  190 + this.hideAppModal()
  191 + }
  192 + }
  193 + };
  194 +</script>
  195 +
  196 +<style lang="scss" scoped>
  197 + .basic-page {
  198 + padding: 0 30rpx;
  199 +
  200 + .basic-header {
  201 + display: flex;
  202 + justify-content: space-between;
  203 + align-items: center;
  204 + height: 140rpx;
  205 + background-color: #fff;
  206 + border-radius: 20rpx;
  207 +
  208 + .basic-text {
  209 + width: 370rpx;
  210 + }
  211 +
  212 + .cu-item {
  213 + background: #3388FF;
  214 + border-radius: 12px;
  215 + width: 120rpx;
  216 + height: 48rpx;
  217 + text-align: center;
  218 + line-height: 40rpx;
  219 +
  220 + text {
  221 + font-size: 12px;
  222 + font-family: PingFangSC-Regular, PingFang SC;
  223 + font-weight: 400;
  224 + color: #FFFFFF;
  225 + }
  226 + }
  227 +
  228 + .basic-text-status {
  229 + font-size: 14px;
  230 + }
  231 + }
  232 +
  233 + .detail {
  234 + background-color: #fff;
  235 + margin-top: 30rpx;
  236 + border-radius: 20rpx;
  237 + width: 690rpx;
  238 +
  239 + .detail-item {
  240 + padding: 30rpx;
  241 + display: flex;
  242 + align-items: center;
  243 +
  244 + .detail-label {
  245 + color: #333;
  246 + font-size: 15px;
  247 + }
  248 +
  249 + .detail-value {
  250 + color: #666;
  251 + font-size: 14px;
  252 + margin-left: 30rpx;
  253 + }
  254 + }
  255 + }
  256 + }
  257 +
  258 + /deep/ .u-modal__content {
  259 + padding: 30rpx 0 !important;
  260 + }
  261 +</style>
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 {
  95 + data
  96 + } = options;
  97 + this.commandDetail = JSON.parse(decodeURIComponent(data));
  98 + if (this.commandDetail.response.status === 'SUCCESS') return
  99 + this.failContent = JSON.stringify(this.commandDetail.response.error)
  100 + }
  101 + };
  102 +</script>
  103 +
  104 +<style lang="scss" scoped>
  105 + .command-detail {
  106 + padding: 0 30rpx;
  107 + height: 100vh;
  108 + background-color: #f8f9fa;
  109 +
  110 + .detail-top {
  111 + height: 118rpx;
  112 + width: 690rpx;
  113 + display: flex;
  114 + align-items: center;
  115 + background-color: #fff;
  116 + color: #333;
  117 + border-radius: 20rpx;
  118 + font-size: 15px;
  119 + margin-top: 30rpx;
  120 + padding: 30rpx;
  121 + }
  122 +
  123 + .detail {
  124 + background-color: #fff;
  125 + margin-top: 30rpx;
  126 + border-radius: 20rpx;
  127 + width: 690rpx;
  128 +
  129 + .detail-item {
  130 + padding: 30rpx;
  131 + display: flex;
  132 + align-items: center;
  133 +
  134 + .detail-label {
  135 + color: #333;
  136 + font-size: 15px;
  137 + }
  138 +
  139 + .detail-value {
  140 + color: #666;
  141 + font-size: 14px;
  142 + margin-left: 30rpx;
  143 + }
  144 + }
  145 + }
  146 +
  147 + .command {
  148 + margin: 30rpx 0;
  149 + }
  150 + }
  151 +</style>
device-subpackage/device-detail/components/command-record.vue renamed from deviceSubPage/deviceDetailPage/tabDetail/CommandRecord.vue
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/components/query-item.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> 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 + @touchmove.stop.prevent="disabledScroll">
  44 + <view class="filter" @touchmove.stop.prevent="disabledScroll">
  45 + <view class="filter-title"><text>筛选条件</text></view>
  46 + <query-item :filterList="issueStatus" title="下发状态"
  47 + @clickTag="currentIndex => handleClickTag(currentIndex, issueStatus)"></query-item>
  48 + <view class="mt-3">
  49 + <uni-datetime-picker return-type="timestamp" v-model="range" type="datetimerange"
  50 + rangeSeparator="至" />
  51 + </view>
  52 + <view class="h-30"></view>
  53 + <view class="button-group">
  54 + <view>
  55 + <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="重置"
  56 + @click="resetFilter"></u-button>
  57 + </view>
  58 + <view>
  59 + <u-button color="#3388ff" shape="circle" text="确认" @click="confirmFilter"></u-button>
  60 + </view>
  61 + </view>
  62 + </view>
  63 + </u-popup>
  64 + </view>
  65 +</template>
  66 +<script>
  67 + import queryItem from '@/pages/device/components/query-item.vue';
  68 + import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
  69 + import {
  70 + formatToDate
  71 + } from '@/plugins/utils.js';
  72 + import {
  73 + debounce
  74 + } from '@/plugins/throttle.js';
  75 + import {
  76 + issueStatus
  77 + } from '../config/data.js'
  78 + import api from '@/api/index.js'
  79 + import {
  80 + useNavigateTo
  81 + } from '@/plugins/utils.js'
  82 +
  83 + export default {
  84 + mixins: [MescrollMixin],
  85 + components: {
  86 + queryItem
  87 + },
  88 + props: {
  89 + tbDeviceId: {
  90 + type: String,
  91 + default: ''
  92 + }
  93 + },
  94 + data() {
  95 + return {
  96 + show: false,
  97 + list: [],
  98 + total: 0,
  99 + range: [],
  100 + formTime: {
  101 + status: '',
  102 + startTime: '',
  103 + endTime: ''
  104 + },
  105 + status: '',
  106 + issueStatus,
  107 + downOption: {
  108 + auto: false //是否在初始化后,自动执行downCallback; 默认true
  109 + },
  110 + page: {
  111 + num: 0,
  112 + size: 10
  113 + }
  114 + };
  115 + },
  116 + methods: {
  117 + formatCommandStatus(status) {
  118 + return status == 'EXPIRED' ?
  119 + 'red' :
  120 + status == 'DELIVERED' ?
  121 + 'blue' :
  122 + status == 'QUEUED' ?
  123 + '#00C9A7' :
  124 + status == 'TIMEOUT' ?
  125 + 'red' :
  126 + status == 'SENT' ?
  127 + '#00C9A7' : ''
  128 + },
  129 + downCallback() {
  130 + for (let i in this.formTime) Reflect.set(this.formTime, i, '')
  131 + this.list = [];
  132 + this.page.num = 1;
  133 + this.loadData(this.page.num, {
  134 + tbDeviceId: this.tbDeviceId
  135 + });
  136 + },
  137 + format(date) {
  138 + return formatToDate(date, 'YYYY-MM-DD HH:mm:ss');
  139 + },
  140 + disabledScroll() {
  141 + return;
  142 + },
  143 + upCallback() {
  144 + const tbDeviceId = {
  145 + tbDeviceId: this.tbDeviceId
  146 + }
  147 + const condition = Object.values(this.formTime)
  148 + if (condition.length === 0) {
  149 + this.page.num += 1;
  150 + this.loadData(this.page.num);
  151 + } else if (condition.filter(Boolean).length > 0) {
  152 + this.page.num += 1;
  153 + this.loadData(this.page.num, {
  154 + ...this.formTime,
  155 + ...tbDeviceId
  156 + });
  157 + } else {
  158 + this.page.num += 1;
  159 + this.loadData(this.page.num);
  160 + }
  161 + },
  162 + async loadData(pageNo, params = {}) {
  163 + let httpData = {
  164 + ...params,
  165 + page: pageNo,
  166 + pageSize: 10
  167 + };
  168 + const res = await api.deviceApi.getRpcRecord({
  169 + params: httpData,
  170 + custom: {
  171 + load: false
  172 + }
  173 + })
  174 + if (!res) return
  175 + this.total = res.total;
  176 + uni.stopPullDownRefresh();
  177 + this.mescroll.endByPage(res.items.length, res.total);
  178 + if (pageNo == 1) {
  179 + this.list = res.items;
  180 + } else {
  181 + this.list = this.list.concat(res.items);
  182 + }
  183 + },
  184 + handleClickTag(currentIndex, list) {
  185 + list.map((item, index) => {
  186 + item.checked = index === currentIndex;
  187 + });
  188 + },
  189 + resetFilter() {
  190 + const {
  191 + issueStatus
  192 + } = this;
  193 + issueStatus.forEach(item => item.checked = false)
  194 + issueStatus[0].checked = true
  195 + },
  196 + close() {
  197 + this.show = false;
  198 + },
  199 + openSearchDialog() {
  200 + this.show = true;
  201 + this.resetFilter()
  202 + this.range = []
  203 + for (let i in this.formTime) Reflect.set(this.formTime, i, '')
  204 + },
  205 + hideKeyboard() {
  206 + uni.hideKeyboard();
  207 + },
  208 + confirmFilter() {
  209 + this.formTime.startTime = this.range[0]
  210 + this.formTime.endTime = this.range[1]
  211 + const issueStatus = this.issueStatus.find(item => item.checked);
  212 + this.formTime.status = issueStatus.type ? issueStatus.type : undefined,
  213 + this.loadData(1, {
  214 + tbDeviceId: this.tbDeviceId,
  215 + ...this.formTime
  216 + });
  217 + this.show = false;
  218 + },
  219 + openCommandDetail(item) {
  220 + useNavigateTo('/device-subpackage/device-detail/components/command-detail?data=', item)
  221 + }
  222 + }
  223 + };
  224 +</script>
  225 +
  226 +<style lang="scss" scoped>
  227 + .command-record {
  228 + padding: 0 30rpx;
  229 + background: #f8f9fa;
  230 +
  231 + .filter-button {
  232 + font-size: 12px;
  233 + width: 160rpx;
  234 + height: 64rpx;
  235 + border-radius: 32rpx;
  236 + display: flex;
  237 + justify-content: center;
  238 + align-items: center;
  239 + background: #f0f1f2;
  240 + color: #666;
  241 +
  242 + image {
  243 + width: 28rpx;
  244 + height: 28rpx;
  245 + margin-left: 4rpx;
  246 + }
  247 + }
  248 + }
  249 +
  250 + .list-item {
  251 + width: 690rpx;
  252 + background-color: #fff;
  253 + border-radius: 20rpx;
  254 + margin: 20rpx auto;
  255 + color: #333;
  256 +
  257 + .item {
  258 + .delivered-color {
  259 + color: blue;
  260 + }
  261 +
  262 + padding: 30rpx;
  263 +
  264 + view {
  265 + font-size: 14px;
  266 + margin-bottom: 10rpx;
  267 + }
  268 +
  269 + .time {
  270 + margin-top: 20rpx;
  271 + color: #999;
  272 + }
  273 +
  274 + .item-first {
  275 + display: flex;
  276 + justify-content: space-between;
  277 + align-items: center;
  278 + font-size: 15px;
  279 + font-weight: 500;
  280 + align-items: center;
  281 +
  282 + .item-right {
  283 + display: flex;
  284 + justify-content: center;
  285 + align-items: center;
  286 + width: 104rpx;
  287 + height: 36rpx;
  288 + font-size: 10px;
  289 + border-radius: 20rpx;
  290 + }
  291 +
  292 + .item-fail {
  293 + color: #848383;
  294 + background-color: #84838325;
  295 + }
  296 +
  297 + .item.success {
  298 + color: #00c9a7;
  299 + background-color: #00c9a725;
  300 + }
  301 + }
  302 + }
  303 + }
  304 +
  305 + .filter {
  306 + padding: 0 30rpx;
  307 +
  308 + .filter-title {
  309 + text-align: center;
  310 + margin-top: 14px;
  311 + font-size: 16px;
  312 + font-weight: 700;
  313 + }
  314 +
  315 + .button-group {
  316 + display: flex;
  317 + margin-top: 40rpx;
  318 + justify-content: space-between;
  319 +
  320 + view {
  321 + width: 330rpx;
  322 + }
  323 + }
  324 + }
  325 +</style>
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 </style> 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 +
  23 + </div>
  24 + </view>
  25 + <view class="button-group">
  26 + <view>
  27 + <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="取消"
  28 + @click="cancelCommand"></u-button>
  29 + </view>
  30 + <view>
  31 + <u-button color="#3388ff" shape="circle" text="确认" @click="confirmCommand"></u-button>
  32 + </view>
  33 + </view>
  34 + </view>
  35 + </u-modal>
  36 + </view>
  37 +</template>
  38 +
  39 +<script>
  40 + import {
  41 + useShowModal
  42 + } from '@/plugins/utils.js'
  43 +
  44 + export default {
  45 + props: {
  46 + showModal: Boolean,
  47 + isShowTCP: Boolean
  48 + },
  49 + data() {
  50 + return {
  51 + current: 0,
  52 + commandType: 'OneWay',
  53 + inputCommandVal: '',
  54 + copyTextValue: {
  55 + "method": "methodThingskit",
  56 + "params": {
  57 + "pin": 7,
  58 + "value": 1
  59 + }
  60 + }
  61 + }
  62 + },
  63 + methods: {
  64 + cancelCommand() {
  65 + this.$emit('cancelCommand')
  66 + },
  67 + confirmCommand() {
  68 + this.$emit('confirmCommand', this.commandType, this.inputCommandVal)
  69 + },
  70 + handleCopy(value) {
  71 + useShowModal(JSON.stringify(value), '命令下发', '复制内容').then(res => {
  72 + uni.setClipboardData({
  73 + data: JSON.stringify(value),
  74 + success: () => {
  75 + uni.showToast({
  76 + title: '复制成功'
  77 + })
  78 + }
  79 + });
  80 + })
  81 + }
  82 + }
  83 + }
  84 +</script>
  85 +
  86 +<style lang="scss" scoped>
  87 + .modal-content {
  88 + width: 720rpx;
  89 + padding: 0 30rpx;
  90 + background-color: white;
  91 +
  92 + .header-title {
  93 + text-align: center;
  94 + font-weight: 700;
  95 + margin-bottom: 40rpx;
  96 + }
  97 +
  98 + .type-text {
  99 + color: #333;
  100 + font-size: 14px;
  101 + font-weight: 700;
  102 + margin-right: 30rpx;
  103 + }
  104 +
  105 + .content-body {
  106 + margin-top: 28rpx;
  107 + width: 100%;
  108 + }
  109 +
  110 + .button-group {
  111 + display: flex;
  112 + margin-top: 40rpx;
  113 + justify-content: space-between;
  114 +
  115 + view {
  116 + width: 300rpx;
  117 + }
  118 + }
  119 + }
  120 +</style>
device-subpackage/device-detail/components/realtime-data.vue 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 -} 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> 52 </style>
  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 +export {
  63 + list,
  64 + issueStatus
  65 +}
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 {
  29 + getDeviceKeys,
  30 + getHistoryData
  31 + } from "./api/index.js";
  32 + import {
  33 + formatToDate
  34 + } from "@/plugins/utils.js";
  35 + import MescrollCompMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mixins/mescroll-comp.js";
  36 + import moment from "moment";
  37 + import base from "@/config/baseUrl.js";
  38 + import {
  39 + list
  40 + } from './config/data.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 + bgColor: '#fff',
  55 + activeColor: {
  56 + fontWeight: 'bold',
  57 + color: '#333',
  58 + },
  59 + inActiveColor: {
  60 + color: '#999',
  61 + },
  62 + list,
  63 + currentTab: 0,
  64 + deviceId: "",
  65 + deviceDetail: {},
  66 + keys: [],
  67 + yesterday: "",
  68 + today: "",
  69 + timeDiff: "",
  70 + historyData: [],
  71 + entityId: "",
  72 + startTs: "",
  73 + endTs: "",
  74 + recordList: [], //实时数据
  75 + isScrollable: false,
  76 + attrList: []
  77 + };
  78 + },
  79 + onUnload() {
  80 + // 页面关闭时,销毁webSocket连接,否则第二次会存在连接不到的情况
  81 + uni.closeSocket();
  82 + },
  83 + async onLoad(options) {
  84 + const {
  85 + id,
  86 + alarmStatus,
  87 + lastOnlineTime,
  88 + tbDeviceId,
  89 + deviceProfileId
  90 + } =
  91 + options;
  92 + this.deviceId = id;
  93 + const res = await uni.$u.http.get(`/yt/device/${id}`);
  94 + this.deviceDetail = {
  95 + ...res,
  96 + alarmStatus,
  97 + lastOnlineTime,
  98 + };
  99 +
  100 + // 设备类型不是网关子设备的添加一个命令记录的选项卡
  101 + if (this.deviceDetail.deviceType !== "SENSOR") {
  102 + this.list.push({
  103 + name: "命令记录",
  104 + });
  105 + }
  106 + this.isScrollable = this.list.length > 4;
  107 + if (res.deviceProfileId) {
  108 + const getAttrList = await uni.$u.http.get(
  109 + `/yt/device/attributes/${res.deviceProfileId}`
  110 + );
  111 + if (Array.isArray(getAttrList)) {
  112 + this.attrList = getAttrList.map(m => {
  113 + return m.identifier
  114 + })
  115 + }
  116 + }
  117 + // 连接webSockte
  118 + const socketTask = uni.connectSocket({
  119 + url: `${base.socketPrefix}://${base.baseWebSocketUrl}/api/ws/plugins/telemetry?token=` +
  120 + uni.getStorageSync("userInfo").isToken, //仅为示例,并非真实接口地址。
  121 + complete: () => {},
  122 + });
  123 + uni.onSocketOpen((header) => {
  124 + socketTask.send({
  125 + data: JSON.stringify({
  126 + attrSubCmds: [],
  127 + tsSubCmds: [{
  128 + entityType: "DEVICE",
  129 + entityId: tbDeviceId,
  130 + scope: "LATEST_TELEMETRY",
  131 + cmdId: 1,
  132 + keys: this.attrList.join(','),
  133 + }, ],
  134 + historyCmds: [],
  135 + entityDataCmds: [],
  136 + entityDataUnsubscribeCmds: [],
  137 + alarmDataCmds: [],
  138 + alarmDataUnsubscribeCmds: [],
  139 + entityCountCmds: [],
  140 + entityCountUnsubscribeCmds: [],
  141 + }),
  142 + success() {},
  143 + });
  144 + });
  145 + socketTask.onMessage((msg) => {
  146 + const {
  147 + data
  148 + } = JSON.parse(msg.data);
  149 + const newArray = [];
  150 + for (const key in data) {
  151 + const [time, value] = data[key].flat(1);
  152 + let obj = {
  153 + key,
  154 + time,
  155 + value,
  156 + };
  157 + if (this.recordList.length === 0) {
  158 + this.recordList.unshift(obj);
  159 + } else {
  160 + newArray.push(obj);
  161 + }
  162 + }
  163 + newArray.forEach((item) => {
  164 + let flag = false;
  165 + this.recordList.forEach((item1) => {
  166 + if (item1.key === item.key) {
  167 + item1.value = item.value;
  168 + item1.time = item.time;
  169 + flag = true;
  170 + }
  171 + });
  172 + if (!flag) {
  173 + this.recordList.unshift(item);
  174 + }
  175 + });
  176 + this.recordList = this.recordList.map((item) => {
  177 + return {
  178 + ...item,
  179 + time: formatToDate(item.time, "YYYY-MM-DD HH:mm:ss"),
  180 + };
  181 + });
  182 + });
  183 +
  184 + const keys = await getDeviceKeys(tbDeviceId);
  185 + this.keys = [keys];
  186 + // 昨天
  187 + this.yesterday = moment().subtract(1, "days").format("YYYY-MM-DD");
  188 + // 今天
  189 + this.today = moment().format("YYYY-MM-DD");
  190 + // 开始时间
  191 + this.startTs = moment().subtract(1, "days").format("x");
  192 + // 结束时间
  193 + this.endTs = moment().format("x");
  194 + this.entityId = tbDeviceId;
  195 +
  196 + const data = await getHistoryData({
  197 + entityId: tbDeviceId,
  198 + startTs: this.startTs,
  199 + endTs: this.endTs,
  200 + keys: keys[0],
  201 + // interval: 1800000,
  202 + limit: 7,
  203 + agg: 'NONE'
  204 + });
  205 + this.timeDiff = "30分钟";
  206 + if (!Object.keys(data).length) return;
  207 +
  208 + this.historyData = data[keys[0]].map((item) => {
  209 + return {
  210 + value: item.value,
  211 + ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
  212 + };
  213 + });
  214 + },
  215 + methods: {
  216 + handleTabClick({
  217 + index
  218 + }) {
  219 + this.currentTab = index;
  220 + },
  221 + handleUpdate(data, e) {
  222 + if (!Array.isArray(data)) {
  223 + this.historyData = [];
  224 + return;
  225 + }
  226 + this.historyData = data.map((item) => {
  227 + return {
  228 + value: item.value,
  229 + ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
  230 + };
  231 + });
  232 + },
  233 + },
  234 + };
  235 +</script>
  236 +
  237 +<style lang="scss" scoped>
  238 + .device-detail-page {
  239 + height: 100vh;
  240 + background-color: #f8f9fa;
  241 + }
  242 +</style>
device-subpackage/device-detail/device-position.vue renamed from deviceSubPage/deviceDetailPage/devicePosition.vue
device-subpackage/device-detail/static/modal.css renamed from deviceSubPage/deviceDetailPage/styles/modal.css
1 -<template>  
2 - <view class="alert-page">  
3 - <view class="filter-button" @click="openSearchDialog">  
4 - <text>筛选</text>  
5 - <image src="../../../static/shaixuan.png" />  
6 - </view>  
7 -  
8 - <mescroll-uni ref="mescrollRef" @init="mescrollInit" :down="downOption" @down="downCallback" @up="upCallback" height="700px">  
9 - <view @click="openDeviceDetail(item)" class="list-item" v-for="(item, index) in list" :key="index">  
10 - <view class="item">  
11 - <view class="item-first">  
12 - <text style="font-weight: bold;">{{ item.deviceName }}</text>  
13 - <view class="item-right">  
14 - <image  
15 - :src="  
16 - item.severity === 'CRITICAL'  
17 - ? '../../../static/danger.png'  
18 - : item.severity === 'MAJOR'  
19 - ? '../../../static/major.png'  
20 - : item.severity === 'MINOR'  
21 - ? '../../../static/secondary.png'  
22 - : item.severity === 'WARNING'  
23 - ? '../../../static/warn.png'  
24 - : '../../../static/noshue.png'  
25 - "  
26 - ></image>  
27 - <text  
28 - :style="{  
29 - color:  
30 - item.severity === 'CRITICAL'  
31 - ? '#DE4437'  
32 - : item.severity === 'MAJOR'  
33 - ? '#DE7337'  
34 - : item.severity === 'MINOR'  
35 - ? '#FFC107'  
36 - : item.severity === 'WARNING'  
37 - ? '#FF1E0B'  
38 - : '#00C9A7'  
39 - }"  
40 - >  
41 - {{  
42 - item.severity === 'CRITICAL'  
43 - ? '危险'  
44 - : item.severity === 'MAJOR'  
45 - ? '重要'  
46 - : item.severity === 'MINOR'  
47 - ? '次要'  
48 - : item.severity === 'WARNING'  
49 - ? '警告'  
50 - : '不确定'  
51 - }}  
52 - </text>  
53 - </view>  
54 - </view>  
55 - <view>  
56 - {{ Object.entries(item.details.data)[0][0] }} :  
57 - <text style="font-weight: bold; margin-left:4rpx;">{{ Object.entries(item.details.data)[0][1] }}</text>  
58 - </view>  
59 - <view v-if="item.status">  
60 - 告警状态:{{  
61 - item.status === 'CLEARED_UNACK'  
62 - ? '清除未确认'  
63 - : item.status === 'CLEARED_ACK'  
64 - ? '清除已确认'  
65 - : item.status === 'ACTIVE_UNACK'  
66 - ? '激活未确认'  
67 - : '激活已确认'  
68 - }}  
69 - </view>  
70 - <view class="time">{{ item.createdTime }}</view>  
71 - </view>  
72 - </view>  
73 - </mescroll-uni>  
74 - <!-- 告警筛选 -->  
75 - <u-popup @close="close" closeable bgColor="#fff" :show="show" mode="bottom" :round="20" @touchmove.stop.prevent="disabledScroll">  
76 - <view class="filter" @touchmove.stop.prevent="disabledScroll">  
77 - <view class="filter-title"><text>筛选条件</text></view>  
78 - <FilterItem :filterList="alarmStatus" title="告警状态" @clickTag="currentIndex => handleClickTag(currentIndex, alarmStatus)"></FilterItem>  
79 - <FilterItem :filterList="typeStatus" title="设备类型" @clickTag="currentIndex => handleClickTag(currentIndex, typeStatus)"></FilterItem>  
80 - <FilterItem :filterList="alarmLevelStatus" title="告警等级" @clickTag="currentIndex => handleClickTag(currentIndex, alarmLevelStatus)"></FilterItem>  
81 - <FilterItem :filterList="timeStatus" title="选择时间" @clickTag="currentIndex => handleClickTag(currentIndex, timeStatus)"></FilterItem>  
82 - <view class="button-group">  
83 - <view><u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="重置" @click="resetFilter"></u-button></view>  
84 - <view><u-button color="#3388ff" shape="circle" text="确认" @click="confirmFilter"></u-button></view>  
85 - </view>  
86 - </view>  
87 - </u-popup>  
88 - <u-calendar  
89 - :show="showCalendar"  
90 - mode="range"  
91 - @confirm="calendarConfirm"  
92 - @close="calendarClose"  
93 - startText="开始时间"  
94 - endText="结束时间"  
95 - confirmDisabledText="请选择日期"  
96 - ></u-calendar>  
97 - </view>  
98 -</template>  
99 -<script>  
100 -import FilterItem from '@/pages/device/components/query-item.vue';  
101 -import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';  
102 -export default {  
103 - mixins: [MescrollMixin],  
104 - components: {  
105 - FilterItem  
106 - },  
107 - props: {  
108 - deviceId: {  
109 - type: String,  
110 - default: ''  
111 - }  
112 - },  
113 - data() {  
114 - return {  
115 - show: false,  
116 - list: [],  
117 - total: '',  
118 - timeData: {  
119 - selectTime: '',  
120 - getTimeGap: ''  
121 - },  
122 - showCalendar: false,  
123 - alarmStatus: [  
124 - {  
125 - checked: true,  
126 - name: '全部',  
127 - type: ''  
128 - },  
129 - {  
130 - checked: false,  
131 - name: '激活未确认',  
132 - type: 'ACTIVE_UNACK'  
133 - },  
134 - {  
135 - checked: false,  
136 - name: '激活已确认',  
137 - type: 'ACTIVE_ACK'  
138 - },  
139 - {  
140 - checked: false,  
141 - name: '清除未确认',  
142 - type: 'CLEARED_UNACK'  
143 - },  
144 - {  
145 - checked: false,  
146 - name: '清除已确认',  
147 - type: 'CLEARED_ACK'  
148 - }  
149 - ],  
150 - typeStatus: [  
151 - {  
152 - checked: true,  
153 - name: '全部',  
154 - type: ''  
155 - },  
156 - {  
157 - checked: false,  
158 - name: '网关设备',  
159 - type: 'GATEWAY'  
160 - },  
161 - {  
162 - checked: false,  
163 - name: '网关子设备',  
164 - type: 'SENSOR'  
165 - },  
166 - {  
167 - checked: false,  
168 - name: '直连设备',  
169 - type: 'DIRECT_CONNECTION'  
170 - }  
171 - ],  
172 - alarmLevelStatus: [  
173 - {  
174 - checked: true,  
175 - name: '全部',  
176 - type: ''  
177 - },  
178 - {  
179 - checked: false,  
180 - name: '危险',  
181 - type: 'CRITICAL'  
182 - },  
183 - {  
184 - checked: false,  
185 - name: '重要',  
186 - type: 'MAJOR'  
187 - },  
188 - {  
189 - checked: false,  
190 - name: '次要',  
191 - type: 'MINOR'  
192 - },  
193 - {  
194 - checked: false,  
195 - name: '警告',  
196 - type: 'WARNING'  
197 - },  
198 - {  
199 - checked: false,  
200 - name: '不确定',  
201 - type: 'INDETERMINATE'  
202 - }  
203 - ],  
204 - timeStatus: [  
205 - {  
206 - checked: true,  
207 - name: '全部',  
208 - type: ''  
209 - },  
210 - {  
211 - checked: false,  
212 - name: '30分钟',  
213 - type: '1800000'  
214 - },  
215 - {  
216 - checked: false,  
217 - name: '一小时',  
218 - type: '3600000'  
219 - },  
220 - {  
221 - checked: false,  
222 - name: '2小时',  
223 - type: '7200000'  
224 - },  
225 - {  
226 - checked: false,  
227 - name: '近一天',  
228 - type: '86400000'  
229 - }  
230 - ],  
231 - downOption: {  
232 - auto: false //是否在初始化后,自动执行downCallback; 默认true  
233 - },  
234 - page: {  
235 - num: 0,  
236 - size: 10  
237 - }  
238 - };  
239 - },  
240 - methods: {  
241 - disabledScroll() {  
242 - return;  
243 - },  
244 - /*下拉刷新的回调 */  
245 - downCallback() {  
246 - //联网加载数据  
247 - this.list = [];  
248 - this.page.num = 1;  
249 - this.loadData(this.page.num, {  
250 - deviceId: this.deviceId  
251 - });  
252 - },  
253 - /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */  
254 - upCallback() {  
255 - //联网加载数据  
256 - this.page.num += 1;  
257 - this.loadData(this.page.num, {  
258 - deviceId: this.deviceId  
259 - });  
260 - },  
261 - //获取告警数据  
262 - loadData(pageNo, params = {}) {  
263 - let httpData = {  
264 - ...params,  
265 - page: pageNo,  
266 - pageSize: 10  
267 - };  
268 - uni.$u.http  
269 - .get('/yt/alarm', {  
270 - params: httpData,  
271 - custom: {  
272 - load: false  
273 - }  
274 - })  
275 - .then(res => {  
276 - this.total = res.total;  
277 - uni.stopPullDownRefresh();  
278 - //方法一(推荐): 后台接口有返回列表的总页数 totalPage  
279 - this.mescroll.endByPage(res.items.length, res.total); //必传参数(当前页的数据个数, 总页数)  
280 - if (pageNo == 1) {  
281 - this.list = res.items;  
282 - } else {  
283 - this.list = this.list.concat(res.items);  
284 - }  
285 - })  
286 - .catch(() => {  
287 - //联网失败, 结束加载  
288 - this.mescroll.endErr();  
289 - });  
290 - },  
291 - handleClickTag(currentIndex, list) {  
292 - list.map((item, index) => {  
293 - item.checked = index === currentIndex;  
294 - });  
295 - },  
296 - resetFilter() {  
297 - const { alarmStatus, typeStatus, alarmLevelStatus, timeStatus } = this;  
298 - [alarmStatus, typeStatus, alarmLevelStatus, timeStatus].forEach(item => item.map((item, index) => (item.checked = index === 0)));  
299 - },  
300 - close() {  
301 - this.show = false;  
302 - },  
303 - openSearchDialog() {  
304 - this.show = true;  
305 - },  
306 - hideKeyboard() {  
307 - uni.hideKeyboard();  
308 - },  
309 - calendarConfirm(e) {  
310 - this.showCalendar = false;  
311 - this.timeData.selectTime = `${e[0]} / ${e[e.length - 1]}`;  
312 - },  
313 - confirmFilter() {  
314 - const alarmState = this.alarmStatus.find(item => item.checked);  
315 - const typeState = this.typeStatus.find(item => item.checked);  
316 - const alarmLevelState = this.alarmLevelStatus.find(item => item.checked);  
317 - const timeState = this.timeStatus.find(item => item.checked);  
318 - const endTs = Date.now();  
319 - const startTs = endTs - timeState.type;  
320 - this.loadData(1, {  
321 - status: alarmState.type ? alarmState.type : undefined,  
322 - deviceType: typeState.type ? typeState.type : undefined,  
323 - severity: alarmLevelState.type ? alarmLevelState.type : undefined,  
324 - startTime: timeState.type ? startTs : undefined,  
325 - endTime: timeState.type ? endTs : undefined,  
326 - deviceId: this.deviceId  
327 - });  
328 - this.show = false;  
329 - },  
330 - calendarClose() {  
331 - this.showCalendar = false;  
332 - },  
333 - openDeviceDetail(item) {  
334 - const { id, deviceName, severity, originatorType, details, createdTime, status } = item;  
335 - let obj = {  
336 - id,  
337 - deviceName,  
338 - severity,  
339 - originatorType,  
340 - details,  
341 - createdTime,  
342 - status  
343 - };  
344 - uni.navigateTo({  
345 - url: '/alarmSubPage/alarmDetailPage/alarmDetail?data=' + JSON.stringify(obj)  
346 - // url: '/' + JSON.stringify(obj)  
347 - });  
348 - }  
349 - }  
350 -};  
351 -</script>  
352 -  
353 -<style lang="scss" scoped>  
354 -.filter-button {  
355 - font-size: 12px;  
356 - width: 160rpx;  
357 - height: 64rpx;  
358 - border-radius: 32rpx;  
359 - display: flex;  
360 - justify-content: center;  
361 - align-items: center;  
362 - background: #f0f1f2;  
363 - color: #666;  
364 - image {  
365 - width: 28rpx;  
366 - height: 28rpx;  
367 - margin-left: 4rpx;  
368 - }  
369 -}  
370 -.alert-page {  
371 - padding: 0 30rpx;  
372 - .list-item {  
373 - width: 690rpx;  
374 - height: 262rpx;  
375 - background-color: #fff;  
376 - border-radius: 20rpx;  
377 - margin: 20rpx auto;  
378 - color: #333;  
379 - .item {  
380 - padding: 30rpx;  
381 - view {  
382 - font-size: 14px;  
383 - margin-bottom: 10rpx;  
384 - }  
385 - .time {  
386 - color: #999;  
387 - }  
388 - .item-first {  
389 - display: flex;  
390 - justify-content: space-between;  
391 - font-size: 15px;  
392 - font-weight: 500;  
393 - align-items: center;  
394 - .item-right {  
395 - display: flex;  
396 - align-items: center;  
397 - image {  
398 - width: 28rpx;  
399 - height: 28rpx;  
400 - margin-right: 10rpx;  
401 - }  
402 - }  
403 - }  
404 - }  
405 - }  
406 -}  
407 -  
408 -.filter {  
409 - padding: 0 30rpx;  
410 - .filter-title {  
411 - text-align: center;  
412 - margin-top: 14px;  
413 - font-size: 16px;  
414 - font-weight: 700;  
415 - }  
416 - .button-group {  
417 - display: flex;  
418 - margin-top: 40rpx;  
419 - justify-content: space-between;  
420 - view {  
421 - width: 330rpx;  
422 - }  
423 - }  
424 -}  
425 -</style>  
feedback-subpackage/feedback/feedback.vue renamed from feedBackSubPage/feedback/feedback.vue
feedback-subpackage/feedback/static/feedback.scss renamed from feedBackSubPage/feedback/static/feedback.scss
login-subpackage/other/code.vue renamed from publicLoginSubPage/other/code.vue
1 -<template>  
2 - <view class="code-page">  
3 - <!-- 公共组件-每个页面必须引入 -->  
4 - <public-module></public-module>  
5 - <view class="login-body">  
6 - <view class="login-phone">  
7 - <view class="phone-main">  
8 - <text class="text">手机验证码登录</text>  
9 - <view class="circleStyle"></view>  
10 - </view>  
11 - <view class="form-row"><u-input v-model="loginForm.phone" type="number" placeholder="请输入手机号码"  
12 - border="bottom"></u-input></view>  
13 - <view class="form-row">  
14 - <u-input type="number" v-model="loginForm.verifyCode" placeholder="请输入验证码" border="bottom">  
15 - <template slot="suffix">  
16 - <view @click="getVerifyCode" class="verify-code">{{ codeText }}</view>  
17 - </template>  
18 - </u-input>  
19 - </view>  
20 - <button class="submit" size="default" @click="onSubmit"><text class="text">登录</text></button>  
21 - <view class="u-flex account-style">  
22 - <view class="content" @click="openAccountFunc">账号密码登录</view>  
23 - </view>  
24 - <view class="circleStyleBottom"></view>  
25 - </view>  
26 - </view>  
27 - </view>  
28 -</template>  
29 -  
30 -<script>  
31 - var clear;  
32 - import {  
33 - mapState,  
34 - mapMutations,  
35 - mapActions  
36 - } from 'vuex';  
37 - import {  
38 - useShowToast,  
39 - useNavigateTo  
40 - } from '@/plugins/utils.js'  
41 - import api from '@/api'  
42 -  
43 - export default {  
44 - data() {  
45 - return {  
46 - loginForm: {  
47 - phone: '',  
48 - verifyCode: ''  
49 - },  
50 - readonly: false,  
51 - codeText: '发送验证码',  
52 - };  
53 - },  
54 - methods: {  
55 - ...mapMutations(['setUserInfo']),  
56 - ...mapActions(['updateBadgeTotal']),  
57 - //验证码按钮文字状态  
58 - codeCountdownText() {  
59 - const _this = this;  
60 - this.readonly = true;  
61 - this.codeText = '60s后重新获取';  
62 - var s = 60;  
63 - clear = setInterval(() => {  
64 - s--;  
65 - _this.codeText = s + 's后重新获取';  
66 - if (s <= 0) {  
67 - clearInterval(clear);  
68 - _this.codeText = '发送验证码';  
69 - _this.readonly = false;  
70 - }  
71 - }, 1000);  
72 - },  
73 - //获取验证码  
74 - async getVerifyCode() {  
75 - const phoneRegular = /^1\d{10}$/;  
76 - if (this.readonly) {  
77 - useShowToast('验证码已发送~')  
78 - }  
79 - if (!this.loginForm.phone) {  
80 - return useShowToast('请输入手机号~')  
81 - }  
82 - if (!phoneRegular.test(this.loginForm.phone)) {  
83 - return useShowToast('手机号格式不正确~')  
84 - }  
85 - // 获取验证码接口  
86 - await api.loginApi.postPhoneCodeApi(this.loginForm.phone)  
87 - this.codeCountdownText(); //开始倒计时  
88 - },  
89 - async onSubmit() {  
90 - const phoneRegular = /^1\d{10}$/;  
91 - const verifyCodeReg = /^\d{6}$/;  
92 - const validateValue = Object.values(this.loginForm)  
93 - if (!validateValue[0]) return uni.$u.toast("请输入手机号码~");  
94 - if (!validateValue[1]) return uni.$u.toast("请输入验证码~");  
95 - if (!phoneRegular.test(validateValue[0])) return uni.$u.toast("手机号格式不正确~");  
96 - if (!verifyCodeReg.test(validateValue[1])) return uni.$u.toast("验证码格式不正确~");  
97 - const res = await api.loginApi.postPhoneLoginApi(this.loginForm)  
98 - if (res) {  
99 - // 储存登录信息  
100 - let tokenInfo = {  
101 - refreshToken: res.refreshToken,  
102 - isToken: res.token  
103 - };  
104 - let userInfo = {  
105 - ...tokenInfo,  
106 - token: true, //token用于判断是否登录  
107 - isThirdLogin: false  
108 - };  
109 - if (userInfo.token) {  
110 - this.setUserInfo(userInfo);  
111 - }  
112 - useShowToast('登录成功~').then(async (res) => {  
113 - this.saveUserInfo();  
114 - await this.getAlarmTotalData();  
115 - useReLaunch("/pages/index/index")  
116 - });  
117 - }  
118 - },  
119 - async getAlarmTotalData() {  
120 - const res = await await api.loginApi.getAlarmTotalApi()  
121 - if (!res) return  
122 - //异步实时更新告警徽标数  
123 - this.updateBadgeTotal(res.totalAlarm?.activedAlarm);  
124 - },  
125 - async saveUserInfo() {  
126 - //储存个人信息  
127 - const res = await api.loginApi.setUserInfoApi()  
128 - if (!res) return  
129 - this.setUserInfo(res);  
130 - },  
131 - openAccountFunc() {  
132 - useNavigateTo('../public/login')  
133 - }  
134 - }  
135 - };  
136 -</script>  
137 -  
138 -<style lang="scss" scoped>  
139 - @import './static/code.scss'; 1 +<template>
  2 + <view class="code-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <view class="login-body">
  6 + <view class="login-phone">
  7 + <view class="phone-main">
  8 + <text class="text">手机验证码登录</text>
  9 + <view class="circleStyle"></view>
  10 + </view>
  11 + <view class="form-row"><u-input v-model="loginForm.phone" type="number" placeholder="请输入手机号码"
  12 + border="bottom"></u-input></view>
  13 + <view class="form-row">
  14 + <u-input type="number" v-model="loginForm.verifyCode" placeholder="请输入验证码" border="bottom">
  15 + <template slot="suffix">
  16 + <view @click="getVerifyCode" class="verify-code">{{ codeText }}</view>
  17 + </template>
  18 + </u-input>
  19 + </view>
  20 + <button class="submit" size="default" @click="onSubmit"><text class="text">登录</text></button>
  21 + <view class="u-flex account-style">
  22 + <view class="content" @click="openAccountFunc">账号密码登录</view>
  23 + </view>
  24 + <view class="circleStyleBottom"></view>
  25 + </view>
  26 + </view>
  27 + </view>
  28 +</template>
  29 +
  30 +<script>
  31 + var clear;
  32 + import {
  33 + mapState,
  34 + mapMutations,
  35 + mapActions
  36 + } from 'vuex';
  37 + import {
  38 + useShowToast,
  39 + useNavigateTo
  40 + } from '@/plugins/utils.js'
  41 + import api from '@/api'
  42 +
  43 + export default {
  44 + data() {
  45 + return {
  46 + loginForm: {
  47 + phone: '',
  48 + verifyCode: ''
  49 + },
  50 + readonly: false,
  51 + codeText: '发送验证码',
  52 + };
  53 + },
  54 + methods: {
  55 + ...mapMutations(['setUserInfo']),
  56 + ...mapActions(['updateBadgeTotal']),
  57 + //验证码按钮文字状态
  58 + codeCountdownText() {
  59 + const _this = this;
  60 + this.readonly = true;
  61 + this.codeText = '60s后重新获取';
  62 + var s = 60;
  63 + clear = setInterval(() => {
  64 + s--;
  65 + _this.codeText = s + 's后重新获取';
  66 + if (s <= 0) {
  67 + clearInterval(clear);
  68 + _this.codeText = '发送验证码';
  69 + _this.readonly = false;
  70 + }
  71 + }, 1000);
  72 + },
  73 + //获取验证码
  74 + async getVerifyCode() {
  75 + const phoneRegular = /^1\d{10}$/;
  76 + if (this.readonly) {
  77 + useShowToast('验证码已发送~')
  78 + }
  79 + if (!this.loginForm.phone) {
  80 + return useShowToast('请输入手机号~')
  81 + }
  82 + if (!phoneRegular.test(this.loginForm.phone)) {
  83 + return useShowToast('手机号格式不正确~')
  84 + }
  85 + // 获取验证码接口
  86 + await api.loginApi.postPhoneCodeApi(this.loginForm.phone)
  87 + this.codeCountdownText(); //开始倒计时
  88 + },
  89 + async onSubmit() {
  90 + const phoneRegular = /^1\d{10}$/;
  91 + const verifyCodeReg = /^\d{6}$/;
  92 + const validateValue = Object.values(this.loginForm)
  93 + if (!validateValue[0]) return uni.$u.toast("请输入手机号码~");
  94 + if (!validateValue[1]) return uni.$u.toast("请输入验证码~");
  95 + if (!phoneRegular.test(validateValue[0])) return uni.$u.toast("手机号格式不正确~");
  96 + if (!verifyCodeReg.test(validateValue[1])) return uni.$u.toast("验证码格式不正确~");
  97 + const res = await api.loginApi.postPhoneLoginApi(this.loginForm)
  98 + if (res) {
  99 + // 储存登录信息
  100 + let tokenInfo = {
  101 + refreshToken: res.refreshToken,
  102 + isToken: res.token
  103 + };
  104 + let userInfo = {
  105 + ...tokenInfo,
  106 + token: true, //token用于判断是否登录
  107 + isThirdLogin: false
  108 + };
  109 + if (userInfo.token) {
  110 + this.setUserInfo(userInfo);
  111 + }
  112 + useShowToast('登录成功~').then(async (res) => {
  113 + this.saveUserInfo();
  114 + await this.getAlarmTotalData();
  115 + useReLaunch("/pages/index/index")
  116 + });
  117 + }
  118 + },
  119 + async getAlarmTotalData() {
  120 + const res = await await api.loginApi.getAlarmTotalApi()
  121 + if (!res) return
  122 + //异步实时更新告警徽标数
  123 + this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
  124 + },
  125 + async saveUserInfo() {
  126 + //储存个人信息
  127 + const res = await api.loginApi.setUserInfoApi()
  128 + if (!res) return
  129 + this.setUserInfo(res);
  130 + },
  131 + openAccountFunc() {
  132 + useNavigateTo('../public/login')
  133 + }
  134 + }
  135 + };
  136 +</script>
  137 +
  138 +<style lang="scss" scoped>
  139 + @import './static/code.scss';
140 </style> 140 </style>
login-subpackage/other/find-password.vue renamed from publicLoginSubPage/other/findPassword.vue
1 -<template>  
2 - <view class="find-password-page">  
3 - <public-module></public-module>  
4 - <view class="top u-flex">  
5 - <view @click="showPhone" :style="{ color: phoneNumberColor }" class="item">1.验证手机号码</view>  
6 - <view :style="{ color: passwordColor }" class="item">2.设置新密码</view>  
7 - </view>  
8 - <view v-if="!nextStatus" class="login-body">  
9 - <view class="login-phone">  
10 - <view class="form-row">  
11 - <u-input v-model="forgetForm.phone" type="number" placeholder="请输入手机号码" border="bottom" />  
12 - </view>  
13 - <view class="form-row">  
14 - <u-input type="number" v-model="forgetForm.verifyCode" placeholder="请输入验证码" border="bottom">  
15 - <template slot="suffix">  
16 - <view @click="getVerifyCode" class="verify-code">{{ codeText }}</view>  
17 - </template>  
18 - </u-input>  
19 - </view>  
20 - <button class="submit" size="default" @click="onNextSubmit"><text style="color:#fff">下一步</text></button>  
21 - </view>  
22 - </view>  
23 - <view v-else class="login-body">  
24 - <view class="login-phone">  
25 - <view class="form-row u-flex">  
26 - <u-input v-model="forgetForm.password" :password="showPassword" placeholder="请设置6-20位新的登录密码" border="bottom">  
27 - <template slot="suffix">  
28 - <view @click="showPasswordMode" style="padding: 10rpx">  
29 - <u-icon width="18" height="14" :name="passwordModeIcon"></u-icon>  
30 - </view>  
31 - </template>  
32 - </u-input>  
33 - </view>  
34 - <view class="form-row u-flex">  
35 - <u-input v-model="forgetForm.repeatPassword" :password="showPasswordRepeat" placeholder="请再次输入新的登录密码" border="bottom">  
36 - <template slot="suffix">  
37 - <view @click="showPasswordModeRepeat" style="padding: 10rpx">  
38 - <u-icon width="18" height="14" :name="passwordModeRepeatIcon"></u-icon>  
39 - </view>  
40 - </template>  
41 - </u-input>  
42 - </view>  
43 - <button class="submit" size="default" @click="onSubmit"><text style="color:#fff">确定</text></button>  
44 - </view>  
45 - </view>  
46 - </view>  
47 -</template>  
48 -  
49 -<script>  
50 - import api from '@/api/index.js'  
51 - import { loginPasswordReg } from '@/plugins/utils.js'  
52 - import { useShowToast,useShowModal,useReLaunch } from '@/plugins/utils.js'  
53 -  
54 - var clear;  
55 - export default {  
56 - data() {  
57 - return {  
58 - forgetForm:{  
59 - phone: '',  
60 - verifyCode: '',  
61 - password: '',  
62 - repeatPassword: '',  
63 - },  
64 - readonly: false,  
65 - codeText: '发送验证码',  
66 - nextStatus: false,  
67 - showPassword: true,  
68 - showPasswordRepeat: true  
69 - };  
70 - },  
71 - computed:{  
72 - phoneNumberColor(){  
73 - return !this.nextStatus ? '#0079fe' : ''  
74 - },  
75 - passwordColor(){  
76 - return !this.nextStatus ? '' : '#0079fe'  
77 - },  
78 - passwordModeIcon(){  
79 - return this.showPassword ? '/static/eye-hide.png' : '/static/eye.png'  
80 - },  
81 - passwordModeRepeatIcon(){  
82 - return this.showPasswordRepeat ? '/static/eye-hide.png' : '/static/eye.png'  
83 - }  
84 - },  
85 - methods: {  
86 - //验证码按钮文字状态  
87 - verifyCodeCountDown() {  
88 - const _this = this;  
89 - this.readonly = true;  
90 - this.codeText = '60s后重新获取';  
91 - var s = 60;  
92 - clear = setInterval(() => {  
93 - s--;  
94 - _this.codeText = s + 's后重新获取';  
95 - if (s <= 0) {  
96 - clearInterval(clear);  
97 - _this.codeText = '发送验证码';  
98 - _this.readonly = false;  
99 - }  
100 - }, 1000);  
101 - },  
102 - //获取验证码  
103 - getVerifyCode() {  
104 - const phoneRegular = /^1\d{10}$/;  
105 - if (this.readonly) {  
106 - useShowToast('验证码已发送~')  
107 - }  
108 - console.log(this.forgetForm.phone);  
109 - if (!this.forgetForm.phone) {  
110 - return useShowToast('请输入手机号~')  
111 - }  
112 - if (!phoneRegular.test(this.forgetForm.phone)) {  
113 - return useShowToast('手机号格式不正确~')  
114 - }  
115 - api.loginApi.postCodeApi(this.forgetForm.phone)  
116 - .then(res => {  
117 - this.verifyCodeCountDown(); //开始倒计时  
118 - })  
119 - },  
120 - onNextSubmit() {  
121 - const phoneRegular = /^1\d{10}$/;  
122 - const verifyCodeReg=/^\d{6}$/;  
123 - const validateValue = Object.values(this.forgetForm)  
124 - if(!validateValue[0]) return uni.$u.toast("请输入手机号码~");  
125 - if(!validateValue[1]) return uni.$u.toast("请输入验证码~");  
126 - if(!phoneRegular.test(validateValue[0])) return uni.$u.toast("手机号格式不正确~");  
127 - if(!verifyCodeReg.test(validateValue[1])) return uni.$u.toast("验证码格式不正确~");  
128 - this.nextStatus = true;  
129 - },  
130 - showPhone() {  
131 - this.nextStatus = false;  
132 - },  
133 - onSubmit() {  
134 - const validateValue = Object.values(this.forgetForm)  
135 - if(!validateValue[2]) return uni.$u.toast("请输入密码~");  
136 - if(!validateValue[3]) return uni.$u.toast("请输入密码~");  
137 - if(!loginPasswordReg.test(validateValue[2])) return useShowModal('密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~')  
138 - if(!loginPasswordReg.test(validateValue[3])) return useShowModal('密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~')  
139 - if (validateValue[2] !== validateValue[3]) return uni.$u.toast('两次输入密码不一致');  
140 - let httpData = {  
141 - password: this.forgetForm.password,  
142 - phoneNumber: this.forgetForm.phone,  
143 - userId: this.forgetForm.verifyCode  
144 - };  
145 - const res = api.loginApi.postResetCodeApi(this.forgetForm.phone, httpData)  
146 - if (res) {  
147 - useShowToast( '重置密码成功~').then(res => {  
148 - useReLaunch('/publicLoginSubPage/public/login')  
149 - });  
150 - }  
151 - },  
152 - showPasswordMode() {  
153 - this.showPassword = !this.showPassword;  
154 - },  
155 - showPasswordModeRepeat() {  
156 - this.showPasswordRepeat = !this.showPasswordRepeat;  
157 - }  
158 - }  
159 - };  
160 -</script>  
161 -  
162 -<style lang="scss" scoped>  
163 - @import './static/findPassword.scss'; 1 +<template>
  2 + <view class="find-password-page">
  3 + <public-module></public-module>
  4 + <view class="top u-flex">
  5 + <view @click="showPhone" :style="{ color: phoneNumberColor }" class="item">1.验证手机号码</view>
  6 + <view :style="{ color: passwordColor }" class="item">2.设置新密码</view>
  7 + </view>
  8 + <view v-if="!nextStatus" class="login-body">
  9 + <view class="login-phone">
  10 + <view class="form-row">
  11 + <u-input v-model="forgetForm.phone" type="number" placeholder="请输入手机号码" border="bottom" />
  12 + </view>
  13 + <view class="form-row">
  14 + <u-input type="number" v-model="forgetForm.verifyCode" placeholder="请输入验证码" border="bottom">
  15 + <template slot="suffix">
  16 + <view @click="getVerifyCode" class="verify-code">{{ codeText }}</view>
  17 + </template>
  18 + </u-input>
  19 + </view>
  20 + <button class="submit" size="default" @click="onNextSubmit"><text style="color:#fff">下一步</text></button>
  21 + </view>
  22 + </view>
  23 + <view v-else class="login-body">
  24 + <view class="login-phone">
  25 + <view class="form-row u-flex">
  26 + <u-input v-model="forgetForm.password" :password="showPassword" placeholder="请设置6-20位新的登录密码" border="bottom">
  27 + <template slot="suffix">
  28 + <view @click="showPasswordMode" style="padding: 10rpx">
  29 + <u-icon width="18" height="14" :name="passwordModeIcon"></u-icon>
  30 + </view>
  31 + </template>
  32 + </u-input>
  33 + </view>
  34 + <view class="form-row u-flex">
  35 + <u-input v-model="forgetForm.repeatPassword" :password="showPasswordRepeat" placeholder="请再次输入新的登录密码" border="bottom">
  36 + <template slot="suffix">
  37 + <view @click="showPasswordModeRepeat" style="padding: 10rpx">
  38 + <u-icon width="18" height="14" :name="passwordModeRepeatIcon"></u-icon>
  39 + </view>
  40 + </template>
  41 + </u-input>
  42 + </view>
  43 + <button class="submit" size="default" @click="onSubmit"><text style="color:#fff">确定</text></button>
  44 + </view>
  45 + </view>
  46 + </view>
  47 +</template>
  48 +
  49 +<script>
  50 + import api from '@/api/index.js'
  51 + import { loginPasswordReg } from '@/plugins/utils.js'
  52 + import { useShowToast,useShowModal,useReLaunch } from '@/plugins/utils.js'
  53 +
  54 + var clear;
  55 + export default {
  56 + data() {
  57 + return {
  58 + forgetForm:{
  59 + phone: '',
  60 + verifyCode: '',
  61 + password: '',
  62 + repeatPassword: '',
  63 + },
  64 + readonly: false,
  65 + codeText: '发送验证码',
  66 + nextStatus: false,
  67 + showPassword: true,
  68 + showPasswordRepeat: true
  69 + };
  70 + },
  71 + computed:{
  72 + phoneNumberColor(){
  73 + return !this.nextStatus ? '#0079fe' : ''
  74 + },
  75 + passwordColor(){
  76 + return !this.nextStatus ? '' : '#0079fe'
  77 + },
  78 + passwordModeIcon(){
  79 + return this.showPassword ? '/static/eye-hide.png' : '/static/eye.png'
  80 + },
  81 + passwordModeRepeatIcon(){
  82 + return this.showPasswordRepeat ? '/static/eye-hide.png' : '/static/eye.png'
  83 + }
  84 + },
  85 + methods: {
  86 + //验证码按钮文字状态
  87 + verifyCodeCountDown() {
  88 + const _this = this;
  89 + this.readonly = true;
  90 + this.codeText = '60s后重新获取';
  91 + var s = 60;
  92 + clear = setInterval(() => {
  93 + s--;
  94 + _this.codeText = s + 's后重新获取';
  95 + if (s <= 0) {
  96 + clearInterval(clear);
  97 + _this.codeText = '发送验证码';
  98 + _this.readonly = false;
  99 + }
  100 + }, 1000);
  101 + },
  102 + //获取验证码
  103 + getVerifyCode() {
  104 + const phoneRegular = /^1\d{10}$/;
  105 + if (this.readonly) {
  106 + useShowToast('验证码已发送~')
  107 + }
  108 + console.log(this.forgetForm.phone);
  109 + if (!this.forgetForm.phone) {
  110 + return useShowToast('请输入手机号~')
  111 + }
  112 + if (!phoneRegular.test(this.forgetForm.phone)) {
  113 + return useShowToast('手机号格式不正确~')
  114 + }
  115 + api.loginApi.postCodeApi(this.forgetForm.phone)
  116 + .then(res => {
  117 + this.verifyCodeCountDown(); //开始倒计时
  118 + })
  119 + },
  120 + onNextSubmit() {
  121 + const phoneRegular = /^1\d{10}$/;
  122 + const verifyCodeReg=/^\d{6}$/;
  123 + const validateValue = Object.values(this.forgetForm)
  124 + if(!validateValue[0]) return uni.$u.toast("请输入手机号码~");
  125 + if(!validateValue[1]) return uni.$u.toast("请输入验证码~");
  126 + if(!phoneRegular.test(validateValue[0])) return uni.$u.toast("手机号格式不正确~");
  127 + if(!verifyCodeReg.test(validateValue[1])) return uni.$u.toast("验证码格式不正确~");
  128 + this.nextStatus = true;
  129 + },
  130 + showPhone() {
  131 + this.nextStatus = false;
  132 + },
  133 + onSubmit() {
  134 + const validateValue = Object.values(this.forgetForm)
  135 + if(!validateValue[2]) return uni.$u.toast("请输入密码~");
  136 + if(!validateValue[3]) return uni.$u.toast("请输入密码~");
  137 + if(!loginPasswordReg.test(validateValue[2])) return useShowModal('密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~')
  138 + if(!loginPasswordReg.test(validateValue[3])) return useShowModal('密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~')
  139 + if (validateValue[2] !== validateValue[3]) return uni.$u.toast('两次输入密码不一致');
  140 + let httpData = {
  141 + password: this.forgetForm.password,
  142 + phoneNumber: this.forgetForm.phone,
  143 + userId: this.forgetForm.verifyCode
  144 + };
  145 + const res = api.loginApi.postResetCodeApi(this.forgetForm.phone, httpData)
  146 + if (res) {
  147 + useShowToast( '重置密码成功~').then(res => {
  148 + useReLaunch('/publicLoginSubPage/public/login')
  149 + });
  150 + }
  151 + },
  152 + showPasswordMode() {
  153 + this.showPassword = !this.showPassword;
  154 + },
  155 + showPasswordModeRepeat() {
  156 + this.showPasswordRepeat = !this.showPasswordRepeat;
  157 + }
  158 + }
  159 + };
  160 +</script>
  161 +
  162 +<style lang="scss" scoped>
  163 + @import './static/findPassword.scss';
164 </style> 164 </style>
login-subpackage/other/set.vue renamed from publicLoginSubPage/other/set.vue
login-subpackage/other/static/code.scss renamed from publicLoginSubPage/other/static/code.scss
1 -.code-page {  
2 - min-height: 100vh;  
3 - background-color: #fff;  
4 - width: 750rpx;  
5 - background: url(/static/login.png) no-repeat;  
6 - background-size: 750rpx 1400rpx;  
7 -}  
8 -.login-body {  
9 - padding: 48rpx 32rpx;  
10 - border-radius: 18rpx 18rpx 0 0;  
11 - z-index: 99;  
12 - position: relative;  
13 -}  
14 -  
15 -.login-phone {  
16 - .phone-main {  
17 - margin-top: 240rpx;  
18 - .text {  
19 - font-size: 22px;  
20 - color: #3a4759;  
21 - position: relative;  
22 - z-index: 9999;  
23 - font-family: PingFangSC-Semibold, PingFang SC;  
24 - font-weight: 600;  
25 - }  
26 - }  
27 - .form-row {  
28 - margin-top: 30rpx;  
29 - .verify-code {  
30 - font-family: PingFangSC-Regular, PingFang SC;  
31 - font-weight: 400;  
32 - font-size: 14px;  
33 - color: #6299ff;  
34 - }  
35 - }  
36 -  
37 - .submit {  
38 - margin-top: 60rpx;  
39 - width: 100%;  
40 - position: relative;  
41 - background: linear-gradient(90deg, #5dc2fc 0%, #377dff 100%);  
42 - border-radius: 46px;  
43 - .text {  
44 - color: #ffffff;  
45 - }  
46 - }  
47 - .account-style {  
48 - flex-direction: row;  
49 - margin-top: 48rpx;  
50 - justify-content: space-between;  
51 - .content {  
52 - color: #999999;  
53 - font-size: 13px;  
54 - }  
55 - } 1 +.code-page {
  2 + min-height: 100vh;
  3 + background-color: #fff;
  4 + width: 750rpx;
  5 + background: url(/static/login.png) no-repeat;
  6 + background-size: 750rpx 1400rpx;
  7 +}
  8 +.login-body {
  9 + padding: 48rpx 32rpx;
  10 + border-radius: 18rpx 18rpx 0 0;
  11 + z-index: 99;
  12 + position: relative;
  13 +}
  14 +
  15 +.login-phone {
  16 + .phone-main {
  17 + margin-top: 240rpx;
  18 + .text {
  19 + font-size: 22px;
  20 + color: #3a4759;
  21 + position: relative;
  22 + z-index: 9999;
  23 + font-family: PingFangSC-Semibold, PingFang SC;
  24 + font-weight: 600;
  25 + }
  26 + }
  27 + .form-row {
  28 + margin-top: 30rpx;
  29 + .verify-code {
  30 + font-family: PingFangSC-Regular, PingFang SC;
  31 + font-weight: 400;
  32 + font-size: 14px;
  33 + color: #6299ff;
  34 + }
  35 + }
  36 +
  37 + .submit {
  38 + margin-top: 60rpx;
  39 + width: 100%;
  40 + position: relative;
  41 + background: linear-gradient(90deg, #5dc2fc 0%, #377dff 100%);
  42 + border-radius: 46px;
  43 + .text {
  44 + color: #ffffff;
  45 + }
  46 + }
  47 + .account-style {
  48 + flex-direction: row;
  49 + margin-top: 48rpx;
  50 + justify-content: space-between;
  51 + .content {
  52 + color: #999999;
  53 + font-size: 13px;
  54 + }
  55 + }
56 } 56 }
login-subpackage/other/static/findPassword.scss renamed from publicLoginSubPage/other/static/findPassword.scss
1 -.find-password-page {  
2 - min-height: 100vh;  
3 - background-color: #fff;  
4 -}  
5 -.top {  
6 - width: 750rpx;  
7 - height: 100rpx;  
8 - border: 0.1px solid #f7f9ff;  
9 - justify-content: space-between;  
10 - flex-direction: row;  
11 - align-items: center;  
12 -  
13 - .item {  
14 - width: 375rpx;  
15 - height: 100rpx;  
16 - border: 0.1px solid #f7f9ff;  
17 - text-align: center;  
18 - line-height: 90rpx;  
19 - }  
20 -}  
21 -  
22 -.login-body {  
23 - margin-top: 40rpx;  
24 - padding: 0 30rpx;  
25 - .login-phone {  
26 - .form-row {  
27 - margin-top: 30rpx;  
28 - .verify-code {  
29 - font-family: PingFangSC-Regular, PingFang SC;  
30 - font-weight: 400;  
31 - font-size: 14px;  
32 - color: #6299ff;  
33 - }  
34 - }  
35 -  
36 - .submit {  
37 - margin-top: 60rpx;  
38 - width: 100%;  
39 - background: #377dff;  
40 - border-radius: 48px;  
41 - }  
42 - } 1 +.find-password-page {
  2 + min-height: 100vh;
  3 + background-color: #fff;
  4 +}
  5 +.top {
  6 + width: 750rpx;
  7 + height: 100rpx;
  8 + border: 0.1px solid #f7f9ff;
  9 + justify-content: space-between;
  10 + flex-direction: row;
  11 + align-items: center;
  12 +
  13 + .item {
  14 + width: 375rpx;
  15 + height: 100rpx;
  16 + border: 0.1px solid #f7f9ff;
  17 + text-align: center;
  18 + line-height: 90rpx;
  19 + }
  20 +}
  21 +
  22 +.login-body {
  23 + margin-top: 40rpx;
  24 + padding: 0 30rpx;
  25 + .login-phone {
  26 + .form-row {
  27 + margin-top: 30rpx;
  28 + .verify-code {
  29 + font-family: PingFangSC-Regular, PingFang SC;
  30 + font-weight: 400;
  31 + font-size: 14px;
  32 + color: #6299ff;
  33 + }
  34 + }
  35 +
  36 + .submit {
  37 + margin-top: 60rpx;
  38 + width: 100%;
  39 + background: #377dff;
  40 + border-radius: 48px;
  41 + }
  42 + }
43 } 43 }
login-subpackage/other/static/set.scss renamed from publicLoginSubPage/other/static/set.scss
login-subpackage/public/login.vue renamed from publicLoginSubPage/public/login.vue
1 -<template>  
2 - <view class="login-page" :style="{backgroundImage:setLoginBg}">  
3 - <!-- 公共组件-每个页面必须引入 -->  
4 - <public-module></public-module>  
5 - <view class="u-flex login-main">  
6 - <view class="content">  
7 - <view class="hello login-text-muted">您好,</view>  
8 - <view style="width: 587rpx;" class="text-clip hello-welcome login-text-muted">欢迎来到{{setHeadTitle}}!</view>  
9 - </view>  
10 - </view>  
11 - <view class="login-body">  
12 - <view class="login-phone">  
13 - <view class="form-row u-flex">  
14 - <u-input :adjust-position="false" v-model="loginForm.username" type="text" placeholder="请输入登录账号"  
15 - border="bottom" />  
16 - </view>  
17 - <view class="form-row u-flex">  
18 - <u-input :adjust-position="false" v-model="loginForm.password" :password="showPassword"  
19 - placeholder="请输入登录密码" border="bottom">  
20 - <template slot="suffix">  
21 - <view @click="showPasswordMode" style="padding: 10rpx">  
22 - <u-icon width="18" height="14" :name="passwordIcon"></u-icon>  
23 - </view>  
24 - </template>  
25 - </u-input>  
26 - </view>  
27 - <button class="submit" size="default" @click="onSubmitFunc">  
28 - <text class="text">登录</text>  
29 - </button>  
30 - <view class="u-flex row-item">  
31 - <view class="row-phone login-text-gray" @click="openCodeFunc">手机验证码登录</view>  
32 - <view class="row-reset login-text-gray" @click="findPassrordFunc">忘记密码</view>  
33 - </view>  
34 - <view class="u-flex link-login">  
35 - <!-- #ifdef MP-WEIXIN -->  
36 - <view class="link-text login-text-gray">第三方账号登录</view>  
37 - <view style="height: 20rpx"></view>  
38 - <button class="link-image" @tap="handleWenxinAuthorization">  
39 - <image class="image" src="/static/weixin.png" mode="aspectFill"></image>  
40 - </button>  
41 - <!-- #endif -->  
42 - </view>  
43 - </view>  
44 - </view>  
45 - </view>  
46 -</template>  
47 -  
48 -<script>  
49 - import {  
50 - mapMutations,  
51 - mapActions,  
52 - mapState  
53 - } from "vuex";  
54 - import api from '@/api'  
55 - import { loginPasswordReg, useReLaunch, useShowToast,useShowModal,useNavigateTo } from '@/plugins/utils.js'  
56 -  
57 - export default {  
58 - data() {  
59 - return {  
60 - loginForm: {  
61 - username: "",  
62 - password: "",  
63 - },  
64 - showPassword: true,  
65 - code: "",  
66 - openid: "",  
67 - mpOwnConfig: {},  
68 - defaultLogo: "/static/login.png",  
69 - };  
70 - },  
71 - onLoad() {  
72 - //#ifdef MP-WEIXIN  
73 - wx.login({  
74 - success: (res) => {  
75 - if(!res.code)return  
76 - this.code = res.code;  
77 - },  
78 - });  
79 - //#endif  
80 - uni.setStorageSync('getConfiguration', {  
81 - isConfiguration: false  
82 - });  
83 - this.getPlateForm();  
84 - },  
85 - computed: {  
86 - passwordIcon(){  
87 - return this.showPassword ? '/static/eye-hide.png' : '/static/eye.png'  
88 - },  
89 - setHeadTitle(){  
90 - return this.mpOwnConfig.name !== undefined ? this.mpOwnConfig.name : "ThingsKit"  
91 - },  
92 - setLoginBg(){  
93 - return 'url('+(this.mpOwnConfig.bg !== undefined ? this.mpOwnConfig.bg : `${this.defaultLogo}`) +')'  
94 - }  
95 - },  
96 - onShow() {  
97 - uni.setStorageSync('getConfiguration', {  
98 - isConfiguration: false  
99 - });  
100 - },  
101 - methods: {  
102 - ...mapState(["plateInfo"]),  
103 - ...mapMutations(["setUserInfo", "setPlateInfo"]),  
104 - ...mapActions(["updateBadgeTotal"]),  
105 - //获取平台定制信息  
106 - async getPlateForm() {  
107 - const res = await api.loginApi.getPlateCustomApi()  
108 - if(!res) return  
109 - this.mpOwnConfig = {  
110 - bg: res.background,  
111 - logo: res.logo,  
112 - name: res.name,  
113 - };  
114 - },  
115 - saveLoginInfo(res,isThirdLoginAndNoDind,isThirdLogin,toastText){  
116 - let tokenInfo = {  
117 - refreshToken: res.refreshToken,  
118 - isToken: res.token,  
119 - };  
120 - let userInfo = {  
121 - ...tokenInfo,  
122 - token: true, //token用于判断是否登录  
123 - isThirdLoginAndNoDind, //用于判断是否是第三方登录并且不需要绑定账号  
124 - isThirdLogin,//用于判断是否是第三方登录并且需要绑定账号  
125 - thirdUserId: res.thirdUserId,  
126 - };  
127 - //设置全局变量openId  
128 - getApp().globalData.openId = res.thirdUserId;  
129 - if (userInfo.token) {  
130 - this.setUserInfo(userInfo);  
131 - }  
132 - useShowToast(toastText).then(async (res) => {  
133 - this.saveUserInfo();  
134 - await this.getAlarmTotalData();  
135 - useReLaunch("/pages/index/index")  
136 - });  
137 - },  
138 - //微信授权登录  
139 - //#ifdef MP-WEIXIN  
140 - handleWenxinAuthorization() {  
141 - wx.getUserProfile({  
142 - desc: "微信第三方授权",  
143 - success: async (reswenxin) => {  
144 - if (  
145 - reswenxin.errMsg === "getUserProfile:ok" &&  
146 - reswenxin.encryptedData  
147 - ) {  
148 - //获取用户信息  
149 - let wenxinUserInfo = {  
150 - avatarUrl: reswenxin.userInfo.avatarUrl,  
151 - thirdUserId: this.openid,  
152 - };  
153 - const res = await api.loginApi.getThirdLoginApi(this.code)  
154 - if (!res.token && res.thirdUserId) {  
155 - //需要进行三方绑定  
156 - let userInfo = {  
157 - isThirdLogin: true, //用于判断是否是第三方登录并且需要绑定账号  
158 - avatar: wenxinUserInfo.avatarUrl,  
159 - thirdUserId: res.thirdUserId,  
160 - };  
161 - this.setUserInfo(userInfo);  
162 - //设置全局变量openId  
163 - getApp().globalData.openId = res.thirdUserId;  
164 - useReLaunch("/pages/index/index")  
165 - } else {  
166 - // 不需要绑定,直接第三方登录使用  
167 - this.saveLoginInfo(res,true,null,"第三方账号登录成功")  
168 - }  
169 - }  
170 - },  
171 - fail: (res) => {  
172 - //拒绝授权  
173 - return;  
174 - },  
175 - });  
176 - },  
177 - //#endif  
178 - async saveUserInfo() {  
179 - const userInfoRes = await api.loginApi.setUserInfoApi()  
180 - const plateInfoRes = await api.loginApi.setPlateInfoApi()  
181 - Promise.all([userInfoRes,plateInfoRes]).then(res=>{  
182 - this.setUserInfo(res[0])  
183 - this.setPlateInfo(res[1])  
184 - })  
185 - },  
186 - async getAlarmTotalData() {  
187 - const res = await api.loginApi.getAlarmTotalApi()  
188 - if(!res) return  
189 - this.updateBadgeTotal(res.totalAlarm?.activedAlarm);  
190 - },  
191 - async onSubmitFunc() {  
192 - const validateValue = Object.values(this.loginForm)  
193 - if(!validateValue[0])return uni.$u.toast("请输入登录账号~");  
194 - if(!validateValue[1])return uni.$u.toast("请输入登录密码~");  
195 - if(!loginPasswordReg.test(validateValue[1])) return useShowModal("密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~",)  
196 - const res = await api.loginApi.postLoginApi(this.loginForm)  
197 - if (res) {  
198 - // 储存登录信息  
199 - this.saveLoginInfo(res,false,false,"登录成功~")  
200 - }  
201 - },  
202 - openCodeFunc() {  
203 - useNavigateTo("../other/code")  
204 - },  
205 - findPassrordFunc() {  
206 - useNavigateTo("../other/findPassword")  
207 - },  
208 - showPasswordMode() {  
209 - this.showPassword = !this.showPassword;  
210 - },  
211 - },  
212 - };  
213 -</script>  
214 -  
215 -<style lang="scss" scoped>  
216 - @import "./static/login.scss";  
217 -  
218 - /deep/ button {  
219 - background: rgba(0, 0, 0, 0);  
220 - }  
221 -</style> 1 +<template>
  2 + <view class="login-page" :style="{backgroundImage:setLoginBg}">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <view class="u-flex login-main">
  6 + <view class="content">
  7 + <view class="hello login-text-muted">您好,</view>
  8 + <view style="width: 587rpx;" class="text-clip hello-welcome login-text-muted">欢迎来到{{setHeadTitle}}!
  9 + </view>
  10 + </view>
  11 + </view>
  12 + <view class="login-body">
  13 + <view class="login-phone">
  14 + <view class="form-row u-flex">
  15 + <u-input :adjust-position="false" v-model="loginForm.username" type="text" placeholder="请输入登录账号"
  16 + border="bottom" />
  17 + </view>
  18 + <view class="form-row u-flex">
  19 + <u-input :adjust-position="false" v-model="loginForm.password" :password="showPassword"
  20 + placeholder="请输入登录密码" border="bottom">
  21 + <template slot="suffix">
  22 + <view @click="showPasswordMode" style="padding: 10rpx">
  23 + <u-icon width="18" height="14" :name="passwordIcon"></u-icon>
  24 + </view>
  25 + </template>
  26 + </u-input>
  27 + </view>
  28 + <button class="submit" size="default" @click="onSubmitFunc">
  29 + <text class="text">登录</text>
  30 + </button>
  31 + <view class="u-flex row-item">
  32 + <view class="row-phone login-text-gray" @click="openCodeFunc">手机验证码登录</view>
  33 + <view class="row-reset login-text-gray" @click="findPassrordFunc">忘记密码</view>
  34 + </view>
  35 + <view class="u-flex link-login">
  36 + <!-- #ifdef MP-WEIXIN -->
  37 + <view class="link-text login-text-gray">第三方账号登录</view>
  38 + <view style="height: 20rpx"></view>
  39 + <button class="link-image" @tap="handleWenxinAuthorization">
  40 + <image class="image" src="/static/weixin.png" mode="aspectFill"></image>
  41 + </button>
  42 + <!-- #endif -->
  43 + </view>
  44 + </view>
  45 + </view>
  46 + </view>
  47 +</template>
  48 +
  49 +<script>
  50 + import {
  51 + mapMutations,
  52 + mapActions,
  53 + mapState
  54 + } from "vuex";
  55 + import api from '@/api'
  56 + import {
  57 + loginPasswordReg,
  58 + useReLaunch,
  59 + useShowToast,
  60 + useShowModal,
  61 + useNavigateTo
  62 + } from '@/plugins/utils.js'
  63 +
  64 + export default {
  65 + data() {
  66 + return {
  67 + loginForm: {
  68 + username: "",
  69 + password: "",
  70 + },
  71 + showPassword: true,
  72 + code: "",
  73 + openid: "",
  74 + mpOwnConfig: {},
  75 + defaultLogo: "/static/login.png",
  76 + };
  77 + },
  78 + onLoad() {
  79 + //#ifdef MP-WEIXIN
  80 + wx.login({
  81 + success: (res) => {
  82 + if (!res.code) return
  83 + this.code = res.code;
  84 + },
  85 + });
  86 + //#endif
  87 + uni.setStorageSync('getConfiguration', {
  88 + isConfiguration: false
  89 + });
  90 + this.getPlateForm();
  91 + },
  92 + computed: {
  93 + passwordIcon() {
  94 + return this.showPassword ? '/static/eye-hide.png' : '/static/eye.png'
  95 + },
  96 + setHeadTitle() {
  97 + return this.mpOwnConfig.name !== undefined ? this.mpOwnConfig.name : "ThingsKit"
  98 + },
  99 + setLoginBg() {
  100 + return 'url(' + (this.mpOwnConfig.bg !== undefined ? this.mpOwnConfig.bg : `${this.defaultLogo}`) + ')'
  101 + }
  102 + },
  103 + onShow() {
  104 + uni.setStorageSync('getConfiguration', {
  105 + isConfiguration: false
  106 + });
  107 + },
  108 + methods: {
  109 + ...mapState(["plateInfo"]),
  110 + ...mapMutations(["setUserInfo", "setPlateInfo"]),
  111 + ...mapActions(["updateBadgeTotal"]),
  112 + //获取平台定制信息
  113 + async getPlateForm() {
  114 + const res = await api.loginApi.getPlateCustomApi()
  115 + if (!res) return
  116 + this.mpOwnConfig = {
  117 + bg: res.background,
  118 + logo: res.logo,
  119 + name: res.name,
  120 + };
  121 + },
  122 + saveLoginInfo(res, isThirdLoginAndNoDind, isThirdLogin, toastText) {
  123 + let tokenInfo = {
  124 + refreshToken: res.refreshToken,
  125 + isToken: res.token,
  126 + };
  127 + let userInfo = {
  128 + ...tokenInfo,
  129 + token: true, //token用于判断是否登录
  130 + isThirdLoginAndNoDind, //用于判断是否是第三方登录并且不需要绑定账号
  131 + isThirdLogin, //用于判断是否是第三方登录并且需要绑定账号
  132 + thirdUserId: res.thirdUserId,
  133 + };
  134 + //设置全局变量openId
  135 + getApp().globalData.openId = res.thirdUserId;
  136 + if (userInfo.token) {
  137 + this.setUserInfo(userInfo);
  138 + }
  139 + useShowToast(toastText).then(async (res) => {
  140 + this.saveUserInfo();
  141 + await this.getAlarmTotalData();
  142 + useReLaunch("/pages/index/index")
  143 + });
  144 + },
  145 + //微信授权登录
  146 + //#ifdef MP-WEIXIN
  147 + handleWenxinAuthorization() {
  148 + wx.getUserProfile({
  149 + desc: "微信第三方授权",
  150 + success: async (reswenxin) => {
  151 + if (
  152 + reswenxin.errMsg === "getUserProfile:ok" &&
  153 + reswenxin.encryptedData
  154 + ) {
  155 + //获取用户信息
  156 + let wenxinUserInfo = {
  157 + avatarUrl: reswenxin.userInfo.avatarUrl,
  158 + thirdUserId: this.openid,
  159 + };
  160 + const res = await api.loginApi.getThirdLoginApi(this.code)
  161 + if (!res.token && res.thirdUserId) {
  162 + //需要进行三方绑定
  163 + let userInfo = {
  164 + isThirdLogin: true, //用于判断是否是第三方登录并且需要绑定账号
  165 + avatar: wenxinUserInfo.avatarUrl,
  166 + thirdUserId: res.thirdUserId,
  167 + };
  168 + this.setUserInfo(userInfo);
  169 + //设置全局变量openId
  170 + getApp().globalData.openId = res.thirdUserId;
  171 + useReLaunch("/pages/index/index")
  172 + } else {
  173 + // 不需要绑定,直接第三方登录使用
  174 + this.saveLoginInfo(res, true, null, "第三方账号登录成功")
  175 + }
  176 + }
  177 + },
  178 + fail: (res) => {
  179 + //拒绝授权
  180 + return;
  181 + },
  182 + });
  183 + },
  184 + //#endif
  185 + async saveUserInfo() {
  186 + const userInfoRes = await api.loginApi.setUserInfoApi()
  187 + const plateInfoRes = await api.loginApi.setPlateInfoApi()
  188 + Promise.all([userInfoRes, plateInfoRes]).then(res => {
  189 + this.setUserInfo(res[0])
  190 + this.setPlateInfo(res[1])
  191 + })
  192 + },
  193 + async getAlarmTotalData() {
  194 + const res = await api.loginApi.getAlarmTotalApi()
  195 + if (!res) return
  196 + this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
  197 + },
  198 + async onSubmitFunc() {
  199 + const validateValue = Object.values(this.loginForm)
  200 + if (!validateValue[0]) return uni.$u.toast("请输入登录账号~");
  201 + if (!validateValue[1]) return uni.$u.toast("请输入登录密码~");
  202 + if (!loginPasswordReg.test(validateValue[1])) return useShowModal(
  203 + "密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~", )
  204 + const res = await api.loginApi.postLoginApi(this.loginForm)
  205 + if (res) {
  206 + // 储存登录信息
  207 + this.saveLoginInfo(res, false, false, "登录成功~")
  208 + }
  209 + },
  210 + openCodeFunc() {
  211 + useNavigateTo("../other/code")
  212 + },
  213 + findPassrordFunc() {
  214 + useNavigateTo("../other/find-password")
  215 + },
  216 + showPasswordMode() {
  217 + this.showPassword = !this.showPassword;
  218 + },
  219 + },
  220 + };
  221 +</script>
  222 +
  223 +<style lang="scss" scoped>
  224 + @import "./static/login.scss";
  225 +
  226 + /deep/ button {
  227 + background: rgba(0, 0, 0, 0);
  228 + }
  229 +</style>
login-subpackage/public/static/login.scss renamed from publicLoginSubPage/public/static/login.scss
1 -.login-page {  
2 - min-height: 100vh;  
3 - width: 750rpx;  
4 - background: url(/static/login.png) no-repeat;  
5 - background-size: 100% 100%;  
6 - .login-main {  
7 - flex-direction: column;  
8 - .content {  
9 - height: 250rpx;  
10 - margin-top: 90rpx;  
11 - margin-left: -107rpx;  
12 - position: relative;  
13 - top: 50rpx;  
14 - left: -15rpx;  
15 - .hello {  
16 - font-size: 30px;  
17 - color: #3a4759;  
18 - z-index: 9999;  
19 - position: relative;  
20 - }  
21 - .hello-welcome {  
22 - position: relative;  
23 - font-size: 30px;  
24 - color: #3a4759;  
25 - z-index: 9999;  
26 - }  
27 - }  
28 - }  
29 -}  
30 -  
31 -.login-body {  
32 - padding: 8rpx 32rpx;  
33 - border-radius: 18rpx 18rpx 0 0;  
34 - z-index: 99;  
35 - position: relative;  
36 -}  
37 -  
38 -.login-phone {  
39 - .form-row {  
40 - justify-content: space-between;  
41 - margin-top: 30rpx;  
42 - }  
43 -  
44 - .submit {  
45 - margin-top: 60rpx;  
46 - background: linear-gradient(90deg, #5dc2fc 0%, #377dff 100%);  
47 - width: 100%;  
48 - border-radius: 46px;  
49 - .text {  
50 - color: #ffffff;  
51 - }  
52 - }  
53 - .row-item {  
54 - flex-direction: row;  
55 - margin-top: 60rpx;  
56 - justify-content: space-between;  
57 - .row-phone {  
58 - color: #999999;  
59 - font-size: 13px;  
60 - }  
61 - .row-reset {  
62 - color: #999999;  
63 - font-size: 13px;  
64 - position: relative;  
65 - }  
66 - }  
67 - .link-login {  
68 - justify-content: center;  
69 - flex-direction: column;  
70 - margin-top: 370rpx;  
71 - /* #ifdef APP-PLUS */  
72 - margin-top: 250rpx;  
73 - /* #endif */  
74 - .link-text {  
75 - color: #999999;  
76 - font-size: 13px;  
77 - }  
78 - .link-image {  
79 - .image {  
80 - width: 75rpx;  
81 - height: 75rpx;  
82 - }  
83 - }  
84 - } 1 +.login-page {
  2 + min-height: 100vh;
  3 + width: 750rpx;
  4 + background: url(/static/login.png) no-repeat;
  5 + background-size: 100% 100%;
  6 + .login-main {
  7 + flex-direction: column;
  8 + .content {
  9 + height: 250rpx;
  10 + margin-top: 90rpx;
  11 + margin-left: -107rpx;
  12 + position: relative;
  13 + top: 50rpx;
  14 + left: -15rpx;
  15 + .hello {
  16 + font-size: 30px;
  17 + color: #3a4759;
  18 + z-index: 9999;
  19 + position: relative;
  20 + }
  21 + .hello-welcome {
  22 + position: relative;
  23 + font-size: 30px;
  24 + color: #3a4759;
  25 + z-index: 9999;
  26 + }
  27 + }
  28 + }
  29 +}
  30 +
  31 +.login-body {
  32 + padding: 8rpx 32rpx;
  33 + border-radius: 18rpx 18rpx 0 0;
  34 + z-index: 99;
  35 + position: relative;
  36 +}
  37 +
  38 +.login-phone {
  39 + .form-row {
  40 + justify-content: space-between;
  41 + margin-top: 30rpx;
  42 + }
  43 +
  44 + .submit {
  45 + margin-top: 60rpx;
  46 + background: linear-gradient(90deg, #5dc2fc 0%, #377dff 100%);
  47 + width: 100%;
  48 + border-radius: 46px;
  49 + .text {
  50 + color: #ffffff;
  51 + }
  52 + }
  53 + .row-item {
  54 + flex-direction: row;
  55 + margin-top: 60rpx;
  56 + justify-content: space-between;
  57 + .row-phone {
  58 + color: #999999;
  59 + font-size: 13px;
  60 + }
  61 + .row-reset {
  62 + color: #999999;
  63 + font-size: 13px;
  64 + position: relative;
  65 + }
  66 + }
  67 + .link-login {
  68 + justify-content: center;
  69 + flex-direction: column;
  70 + margin-top: 370rpx;
  71 + /* #ifdef APP-PLUS */
  72 + margin-top: 250rpx;
  73 + /* #endif */
  74 + .link-text {
  75 + color: #999999;
  76 + font-size: 13px;
  77 + }
  78 + .link-image {
  79 + .image {
  80 + width: 75rpx;
  81 + height: 75rpx;
  82 + }
  83 + }
  84 + }
85 } 85 }
@@ -49,10 +49,9 @@ @@ -49,10 +49,9 @@
49 "style": { 49 "style": {
50 "navigationBarTitleText": "查看组态" 50 "navigationBarTitleText": "查看组态"
51 } 51 }
52 -  
53 }, 52 },
54 { 53 {
55 - "path": "pages/index/configuration/configurationDetail", 54 + "path": "pages/index/configuration/configuration-detail",
56 "style": { 55 "style": {
57 "navigationBarTitleText": "组态详情" 56 "navigationBarTitleText": "组态详情"
58 } 57 }
@@ -65,30 +64,30 @@ @@ -65,30 +64,30 @@
65 } 64 }
66 ], 65 ],
67 "subPackages": [{ 66 "subPackages": [{
68 - "root": "alarmSubPage", 67 + "root": "alarm-subpackage",
69 "pages": [{ 68 "pages": [{
70 - "path": "alarmDetailPage/alarmDetail", 69 + "path": "alarm-detail/alarm-detail",
71 "style": { 70 "style": {
72 "navigationBarTitleText": "告警详情" 71 "navigationBarTitleText": "告警详情"
73 } 72 }
74 }] 73 }]
75 }, 74 },
76 { 75 {
77 - "root": "deviceSubPage", 76 + "root": "device-subpackage",
78 "pages": [{ 77 "pages": [{
79 - "path": "deviceDetailPage/deviceDetail", 78 + "path": "device-detail/device-detail",
80 "style": { 79 "style": {
81 "navigationBarTitleText": "设备详情" 80 "navigationBarTitleText": "设备详情"
82 } 81 }
83 }, 82 },
84 { 83 {
85 - "path": "deviceDetailPage/tabDetail/CommandDetail", 84 + "path": "device-detail/components/command-detail",
86 "style": { 85 "style": {
87 "navigationBarTitleText": "命令详情" 86 "navigationBarTitleText": "命令详情"
88 } 87 }
89 }, 88 },
90 { 89 {
91 - "path": "deviceDetailPage/devicePosition", 90 + "path": "device-detail/device-position",
92 "style": { 91 "style": {
93 "navigationBarTitleText": "设备地理位置" 92 "navigationBarTitleText": "设备地理位置"
94 }, 93 },
@@ -104,15 +103,15 @@ @@ -104,15 +103,15 @@
104 ] 103 ]
105 }, 104 },
106 { 105 {
107 - "root": "sysNotifySubPage", 106 + "root": "sysnotify-subpackage",
108 "pages": [{ 107 "pages": [{
109 - "path": "sysNotifyPage/systemNotify", 108 + "path": "sys-notify/system-notify",
110 "style": { 109 "style": {
111 "navigationBarTitleText": "系统通知" 110 "navigationBarTitleText": "系统通知"
112 } 111 }
113 }, 112 },
114 { 113 {
115 - "path": "sysNotifyPage/notifyDetail", 114 + "path": "sys-notify/notify-detail",
116 "style": { 115 "style": {
117 "navigationBarTitleText": "通知详情" 116 "navigationBarTitleText": "通知详情"
118 } 117 }
@@ -120,7 +119,7 @@ @@ -120,7 +119,7 @@
120 ] 119 ]
121 }, 120 },
122 { 121 {
123 - "root": "feedBackSubPage", 122 + "root": "feedback-subpackage",
124 "pages": [{ 123 "pages": [{
125 "path": "feedback/feedback", 124 "path": "feedback/feedback",
126 "style": { 125 "style": {
@@ -129,7 +128,7 @@ @@ -129,7 +128,7 @@
129 }] 128 }]
130 }, 129 },
131 { 130 {
132 - "root": "publicLoginSubPage", 131 + "root": "login-subpackage",
133 "pages": [{ 132 "pages": [{
134 "path": "public/login", 133 "path": "public/login",
135 "style": { 134 "style": {
@@ -149,7 +148,7 @@ @@ -149,7 +148,7 @@
149 } 148 }
150 }, 149 },
151 { 150 {
152 - "path": "other/findPassword", 151 + "path": "other/find-password",
153 "style": { 152 "style": {
154 "navigationBarTitleText": "忘记密码" 153 "navigationBarTitleText": "忘记密码"
155 } 154 }
@@ -159,7 +158,7 @@ @@ -159,7 +158,7 @@
159 ], 158 ],
160 "globalStyle": { 159 "globalStyle": {
161 "navigationBarTextStyle": "black", 160 "navigationBarTextStyle": "black",
162 - "navigationBarTitleText": "云腾app", 161 + "navigationBarTitleText": "ThingsKit",
163 "navigationBarBackgroundColor": "#FFFFFF", 162 "navigationBarBackgroundColor": "#FFFFFF",
164 "backgroundColor": "#FFFFFF", 163 "backgroundColor": "#FFFFFF",
165 "backgroundColorTop": "#FFFFFF" 164 "backgroundColorTop": "#FFFFFF"
@@ -140,8 +140,8 @@ @@ -140,8 +140,8 @@
140 downCallback() { 140 downCallback() {
141 this.list.length = 0; 141 this.list.length = 0;
142 this.page.num = 1; 142 this.page.num = 1;
143 - this.loadData(this.page.num);  
144 this.resetQuery(); 143 this.resetQuery();
  144 + this.loadData(this.page.num);
145 }, 145 },
146 //上拉加载 146 //上拉加载
147 upCallback() { 147 upCallback() {
@@ -192,7 +192,7 @@ @@ -192,7 +192,7 @@
192 }, 192 },
193 //跳转告警详情 193 //跳转告警详情
194 openAlertDetail(e) { 194 openAlertDetail(e) {
195 - useNavigateTo('/alarmSubPage/alarmDetailPage/alarmDetail?data=', e) 195 + useNavigateTo('/alarm-subpackage/alarm-detail/alarm-detail?data=', e)
196 }, 196 },
197 } 197 }
198 }; 198 };
@@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
11 @up="upCallback"> 11 @up="upCallback">
12 <device-item :list="list" @openDeviceDetail="openDeviceDetail"></device-item> 12 <device-item :list="list" @openDeviceDetail="openDeviceDetail"></device-item>
13 </mescroll-body> 13 </mescroll-body>
  14 + <view style="height: 20rpx"></view>
14 <!-- 设备筛选 --> 15 <!-- 设备筛选 -->
15 <device-popup ref="devicePopupRef" :show="show" @close="close" 16 <device-popup ref="devicePopupRef" :show="show" @close="close"
16 @queryCondition="getQueryCondition"></device-popup> 17 @queryCondition="getQueryCondition"></device-popup>
@@ -173,7 +174,7 @@ @@ -173,7 +174,7 @@
173 }, 174 },
174 openDeviceDetail(id, alarmStatus, lastOnlineTime, tbDeviceId) { 175 openDeviceDetail(id, alarmStatus, lastOnlineTime, tbDeviceId) {
175 uni.navigateTo({ 176 uni.navigateTo({
176 - url: `/deviceSubPage/deviceDetailPage/deviceDetail?id=${id}&alarmStatus=${alarmStatus}&lastOnlineTime=${lastOnlineTime}&tbDeviceId=${tbDeviceId}` 177 + url: `/device-subpackage/device-detail/device-detail?id=${id}&alarmStatus=${alarmStatus}&lastOnlineTime=${lastOnlineTime}&tbDeviceId=${tbDeviceId}`
177 }); 178 });
178 }, 179 },
179 } 180 }
pages/index/configuration/configuration-detail.vue renamed from pages/index/configuration/configurationDetail.vue
1 -<template>  
2 - <view class="content">  
3 - <!-- 公共组件-每个页面必须引入 -->  
4 - <public-module></public-module>  
5 - <web-view :src="showConfiguration" bindload="bindload" binderror="binderror"></web-view>  
6 - </view>  
7 -</template>  
8 -  
9 -<script>  
10 - export default {  
11 - data() {  
12 - return {  
13 - showConfiguration: '',  
14 - params: ''  
15 - };  
16 - },  
17 - onLoad(e) {  
18 - uni.setStorageSync('getConfiguration', {  
19 - isConfiguration: true  
20 - });  
21 - if (e.configId !== null) {  
22 - this.params = e.configId;  
23 - this.requestUrl(this.params);  
24 - }  
25 - // 隐藏原生的tabbar  
26 - uni.hideTabBar();  
27 - },  
28 - onShow() {  
29 - uni.setStorageSync('getConfiguration', {  
30 - isConfiguration: true  
31 - });  
32 - },  
33 - onUnload() {  
34 - uni.setStorageSync('getConfiguration', {  
35 - isConfiguration: false  
36 - });  
37 - uni.removeStorageSync('config');  
38 - },  
39 - methods: {  
40 - // 网页加载成功时候触发此事件  
41 - bindload(res) {  
42 - console.log(res, res.detail);  
43 - },  
44 - // 网页加载失败的时候触发此事件  
45 - binderror(err) {  
46 - console.log(err, err.detail);  
47 - },  
48 - async requestUrl(e) {  
49 - const httpData = {  
50 - configurationId: e,  
51 - lightbox: 1  
52 - };  
53 - const getUrl = await uni.$u.http.get('/', {  
54 - params: httpData,  
55 - custom: {  
56 - load: false  
57 - }  
58 - });  
59 - const pathUrl = uni.getStorageSync('config');  
60 - const userInfo = uni.getStorageSync('userInfo')  
61 - this.showConfiguration =  
62 - `${pathUrl.baseURL}/?configurationId=${e}&userId=${userInfo.userId}&lightbox=1`  
63 - }  
64 - }  
65 - };  
66 -</script>  
67 -  
68 -<style lang="scss" scoped>  
69 - .content {  
70 - background: #f8f9fa;  
71 - min-height: 100vh;  
72 - } 1 +<template>
  2 + <view class="content">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <web-view :src="showConfiguration" bindload="bindload" binderror="binderror"></web-view>
  6 + </view>
  7 +</template>
  8 +
  9 +<script>
  10 + export default {
  11 + data() {
  12 + return {
  13 + showConfiguration: '',
  14 + params: ''
  15 + };
  16 + },
  17 + onLoad(e) {
  18 + uni.setStorageSync('getConfiguration', {
  19 + isConfiguration: true
  20 + });
  21 + if (e.configId !== null) {
  22 + this.params = e.configId;
  23 + this.requestUrl(this.params);
  24 + }
  25 + // 隐藏原生的tabbar
  26 + uni.hideTabBar();
  27 + },
  28 + onShow() {
  29 + uni.setStorageSync('getConfiguration', {
  30 + isConfiguration: true
  31 + });
  32 + },
  33 + onUnload() {
  34 + uni.setStorageSync('getConfiguration', {
  35 + isConfiguration: false
  36 + });
  37 + uni.removeStorageSync('config');
  38 + },
  39 + methods: {
  40 + // 网页加载成功时候触发此事件
  41 + bindload(res) {
  42 + console.log(res, res.detail);
  43 + },
  44 + // 网页加载失败的时候触发此事件
  45 + binderror(err) {
  46 + console.log(err, err.detail);
  47 + },
  48 + async requestUrl(e) {
  49 + const httpData = {
  50 + configurationId: e,
  51 + lightbox: 1
  52 + };
  53 + const getUrl = await uni.$u.http.get('/', {
  54 + params: httpData,
  55 + custom: {
  56 + load: false
  57 + }
  58 + });
  59 + const pathUrl = uni.getStorageSync('config');
  60 + const userInfo = uni.getStorageSync('userInfo')
  61 + this.showConfiguration =
  62 + `${pathUrl.baseURL}/?configurationId=${e}&userId=${userInfo.userId}&lightbox=1`
  63 + }
  64 + }
  65 + };
  66 +</script>
  67 +
  68 +<style lang="scss" scoped>
  69 + .content {
  70 + background: #f8f9fa;
  71 + min-height: 100vh;
  72 + }
73 </style> 73 </style>
@@ -71,7 +71,7 @@ @@ -71,7 +71,7 @@
71 }, 71 },
72 openConfigDetail(e) { 72 openConfigDetail(e) {
73 uni.navigateTo({ 73 uni.navigateTo({
74 - url: 'configurationDetail?configId=' + e 74 + url: 'configuration-detail?configId=' + e
75 }); 75 });
76 }, 76 },
77 /*下拉刷新的回调 */ 77 /*下拉刷新的回调 */
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 export default { 17 export default {
18 data() { 18 data() {
19 return { 19 return {
20 - staticLogo: '../../static/logo.png', 20 + staticLogo: '/static/logo.png',
21 mpOwnConfig: {} 21 mpOwnConfig: {}
22 }; 22 };
23 }, 23 },
@@ -30,7 +30,7 @@ @@ -30,7 +30,7 @@
30 created() { 30 created() {
31 if (!this.userInfo.isToken) { 31 if (!this.userInfo.isToken) {
32 setTimeout(() => { 32 setTimeout(() => {
33 - useReLaunch('/publicLoginSubPage/public/login') 33 + useReLaunch('/login-subpackage/public/login')
34 }, 1000); 34 }, 1000);
35 } else { 35 } else {
36 setTimeout(() => { 36 setTimeout(() => {
@@ -97,13 +97,13 @@ @@ -97,13 +97,13 @@
97 showLogout: false, 97 showLogout: false,
98 thirdObj: {}, 98 thirdObj: {},
99 systemList: [{ 99 systemList: [{
100 - url: '/sysNotifySubPage/sysNotifyPage/systemNotify', 100 + url: '/sysnotify-subpackage/sys-notify/system-notify',
101 text: '系统通知', 101 text: '系统通知',
102 leftIcon: '/static/sys-not.png', 102 leftIcon: '/static/sys-not.png',
103 rightIcon: '/static/arrow-right.png' 103 rightIcon: '/static/arrow-right.png'
104 }, 104 },
105 { 105 {
106 - url: '/feedBackSubPage/feedback/feedback', 106 + url: '/feedback-subpackage/feedback/feedback',
107 text: '意见反馈', 107 text: '意见反馈',
108 leftIcon: '/static/find-sugg.png', 108 leftIcon: '/static/find-sugg.png',
109 rightIcon: '/static/arrow-right.png' 109 rightIcon: '/static/arrow-right.png'
@@ -139,14 +139,14 @@ @@ -139,14 +139,14 @@
139 useNavigateTo(url) 139 useNavigateTo(url)
140 }, 140 },
141 navigatorLogin() { 141 navigatorLogin() {
142 - useNavigateTo('/publicLoginSubPage/public/login') 142 + useNavigateTo('/login-subpackage/public/login')
143 }, 143 },
144 navigatorPersonal() { 144 navigatorPersonal() {
145 let data = { 145 let data = {
146 data: this.userInfo, 146 data: this.userInfo,
147 third: this.thirdObj 147 third: this.thirdObj
148 }; 148 };
149 - useNavigateTo('/publicLoginSubPage/other/set?data=', data) 149 + useNavigateTo('/login-subpackage/other/set?data=', data)
150 }, 150 },
151 openBindAccountModal() { 151 openBindAccountModal() {
152 this.showBindAccount = true; 152 this.showBindAccount = true;
@@ -164,7 +164,7 @@ @@ -164,7 +164,7 @@
164 if (res.confirm) { 164 if (res.confirm) {
165 that.emptyUserInfo(); 165 that.emptyUserInfo();
166 that.showLogout = false; 166 that.showLogout = false;
167 - useNavigateTo('/publicLoginSubPage/public/login') 167 + useNavigateTo('/login-subpackage/public/login')
168 } 168 }
169 }) 169 })
170 }, 170 },
@@ -47,6 +47,34 @@ button { @@ -47,6 +47,34 @@ button {
47 white-space: nowrap; 47 white-space: nowrap;
48 } 48 }
49 ///////////////////////////////////////////////////////////////小程序、app抽取公共样式///////////////////////////////////////////////////////////////////// 49 ///////////////////////////////////////////////////////////////小程序、app抽取公共样式/////////////////////////////////////////////////////////////////////
  50 +.mt-3{
  51 + margin-top: 30rpx;
  52 +}
  53 +.ml-16{
  54 + margin-left: 16rpx;
  55 +}
  56 +.mt-15{
  57 + margin-top: 15rpx;
  58 +}
  59 +.mr-2{
  60 + margin-right: 20rpx;
  61 +}
  62 +.ml-3{
  63 + margin-top: 20rpx;
  64 +}
  65 +.ml-1{
  66 + margin-left: 10rpx;
  67 +}
  68 +.pl-3{
  69 + padding-left: 30rpx;
  70 +}
  71 +.w-100{
  72 + width: 100%;
  73 +}
  74 +.h-30{
  75 + height:30rpx;
  76 +}
  77 +
50 //通用(设备、告警、摄像头分页头部组织和设备数和设备、告警里面的详情(左边的文本) 78 //通用(设备、告警、摄像头分页头部组织和设备数和设备、告警里面的详情(左边的文本)
51 .ml-10{ 79 .ml-10{
52 margin-left: 10rpx; 80 margin-left: 10rpx;
sysnotify-subpackage/sys-notify/notify-detail.vue renamed from sysNotifySubPage/sysNotifyPage/notifyDetail.vue
1 -<template>  
2 - <view class="notify-detail-page">  
3 - <!-- 公共组件-每个页面必须引入 -->  
4 - <public-module></public-module>  
5 - <view class="notify-column">  
6 - <view class="column-list">  
7 - <view class="column-title">  
8 - <view class="text-clip" style="width:500rpx">  
9 - <text class="notify-detail-text ">{{ notifyList.title }}</text>  
10 - </view>  
11 - </view>  
12 - <view style="height: 25rpx;"></view>  
13 - <u-list height="140rpx">  
14 - <u-list-item>  
15 - <u-cell :value="`${notifyList.senderDate}`" :title="`${notifyList.senderName}`">  
16 - <u-avatar slot="icon" shape="circle" size="35" :src="notifyList.avatar"></u-avatar>  
17 - </u-cell>  
18 - </u-list-item>  
19 - </u-list>  
20 - <view class="bottom-text">  
21 - <view class="u-flex column"></view>  
22 - <view style="margin-top: 21rpx;">  
23 - <!-- 富文本解析 -->  
24 - <u-parse :content="notifyList.content"></u-parse>  
25 - <!-- 富文本解析 -->  
26 - </view>  
27 - </view>  
28 - </view>  
29 - </view>  
30 - </view>  
31 -</template>  
32 -  
33 -<script>  
34 -export default {  
35 - data() {  
36 - return {  
37 - notifyList: {}  
38 - };  
39 - },  
40 - onLoad(e) {  
41 - // 隐藏原生的tabbar  
42 - uni.hideTabBar();  
43 - if (e.data !== null) {  
44 - let params = JSON.parse(decodeURIComponent(e.data));  
45 - if (params) this.notifyList = params;  
46 - }  
47 - }  
48 -};  
49 -</script>  
50 -  
51 -<style lang="scss" scoped>  
52 -@import './static/notifyDetail.scss';  
53 -/deep/ .u-line {  
54 - position: relative !important;  
55 - width: 640rpx !important;  
56 - left: 31rpx !important;  
57 -}  
58 -</style> 1 +<template>
  2 + <view class="notify-detail-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <view class="notify-column">
  6 + <view class="column-list">
  7 + <view class="column-title">
  8 + <view class="text-clip" style="width:500rpx">
  9 + <text class="notify-detail-text ">{{ notifyList.title }}</text>
  10 + </view>
  11 + </view>
  12 + <view style="height: 25rpx;"></view>
  13 + <u-list height="140rpx">
  14 + <u-list-item>
  15 + <u-cell :value="`${notifyList.senderDate}`" :title="`${notifyList.senderName}`">
  16 + <u-avatar slot="icon" shape="circle" size="35" :src="notifyList.avatar"></u-avatar>
  17 + </u-cell>
  18 + </u-list-item>
  19 + </u-list>
  20 + <view class="bottom-text">
  21 + <view class="u-flex column"></view>
  22 + <view style="margin-top: 21rpx;">
  23 + <!-- 富文本解析 -->
  24 + <u-parse :content="notifyList.content"></u-parse>
  25 + <!-- 富文本解析 -->
  26 + </view>
  27 + </view>
  28 + </view>
  29 + </view>
  30 + </view>
  31 +</template>
  32 +
  33 +<script>
  34 +export default {
  35 + data() {
  36 + return {
  37 + notifyList: {}
  38 + };
  39 + },
  40 + onLoad(e) {
  41 + // 隐藏原生的tabbar
  42 + uni.hideTabBar();
  43 + if (e.data !== null) {
  44 + let params = JSON.parse(decodeURIComponent(e.data));
  45 + if (params) this.notifyList = params;
  46 + }
  47 + }
  48 +};
  49 +</script>
  50 +
  51 +<style lang="scss" scoped>
  52 +@import './static/notifyDetail.scss';
  53 +/deep/ .u-line {
  54 + position: relative !important;
  55 + width: 640rpx !important;
  56 + left: 31rpx !important;
  57 +}
  58 +</style>
sysnotify-subpackage/sys-notify/static/notifyDetail.scss renamed from sysNotifySubPage/sysNotifyPage/static/notifyDetail.scss
1 -.notify-detail-page {  
2 - min-height: 100vh;  
3 - background-color: #f8f9fa;  
4 - padding-top: 10rpx;  
5 - padding-left: 27rpx;  
6 -}  
7 -.notify-column {  
8 - .column-list {  
9 - border-radius: 10px;  
10 - margin-top: 20rpx;  
11 - width: 696rpx;  
12 - height: 1000rpx;  
13 - background-color: #ffffff;  
14 - .column-title{  
15 - margin-left: 36rpx;  
16 - position: relative;  
17 - top: 21rpx;  
18 - }  
19 - .bottom-text {  
20 - margin-left: 33rpx;  
21 - .text {  
22 - color: #2e384d;  
23 - font-size: 15px;  
24 - }  
25 - .column {  
26 - margin-top: 20rpx;  
27 - .text {  
28 - font-size: 16px;  
29 - font-family: PingFangSC-Medium, PingFang SC;  
30 - font-weight: 500;  
31 - color: #2e384d;  
32 - }  
33 - }  
34 - }  
35 - }  
36 -} 1 +.notify-detail-page {
  2 + min-height: 100vh;
  3 + background-color: #f8f9fa;
  4 + padding-top: 10rpx;
  5 + padding-left: 27rpx;
  6 +}
  7 +.notify-column {
  8 + .column-list {
  9 + border-radius: 10px;
  10 + margin-top: 20rpx;
  11 + width: 696rpx;
  12 + height: 1000rpx;
  13 + background-color: #ffffff;
  14 + .column-title{
  15 + margin-left: 36rpx;
  16 + position: relative;
  17 + top: 21rpx;
  18 + }
  19 + .bottom-text {
  20 + margin-left: 33rpx;
  21 + .text {
  22 + color: #2e384d;
  23 + font-size: 15px;
  24 + }
  25 + .column {
  26 + margin-top: 20rpx;
  27 + .text {
  28 + font-size: 16px;
  29 + font-family: PingFangSC-Medium, PingFang SC;
  30 + font-weight: 500;
  31 + color: #2e384d;
  32 + }
  33 + }
  34 + }
  35 + }
  36 +}
sysnotify-subpackage/sys-notify/static/systemNotify.scss renamed from sysNotifySubPage/sysNotifyPage/static/systemNotify.scss
1 -.notify-page {  
2 - min-height: 100vh;  
3 - background-color: #f8f9fa;  
4 - padding-top: 10rpx;  
5 - padding-left: 27rpx;  
6 - .notify-page-top-select{  
7 - width: 750rpx;  
8 - height:53rpx;  
9 - background-color: #f8f9fa;  
10 - position:fixed;  
11 - top:0;  
12 - z-index: 99999;  
13 - }  
14 - .top-select{  
15 - background-color: #f8f9fa;  
16 - width: 700rpx;  
17 - position: relative;  
18 - top: 35rpx;  
19 - }  
20 -}  
21 -/deep/.u-list-item {  
22 - background: #ffffff;  
23 - width: 695rpx;  
24 - border-radius: 10px;  
25 - margin-top: 20rpx;  
26 -}  
27 -.notify-main {  
28 - .main {  
29 - flex-direction: column;  
30 - margin-left: -22rpx;  
31 - .main-item {  
32 - justify-content: space-between;  
33 - flex-direction: row;  
34 - margin: 15rpx;  
35 - width: 700rpx;  
36 - height: 136rpx;  
37 - background: #ffffff;  
38 - border-radius: 10px;  
39 - .item {  
40 - justify-content: space-between;  
41 - flex-direction: row;  
42 - .item-avatar {  
43 - margin-left: 30rpx;  
44 - .avatar {  
45 - margin-left: 30rpx;  
46 - }  
47 - }  
48 - .item-content {  
49 - margin-left: 30rpx;  
50 - text-align: left;  
51 - justify-content: space-between;  
52 - flex-direction: column;  
53 - .text-top {  
54 - width: 400rpx;  
55 - color: #2e384d;  
56 - font-size: 15px;  
57 - }  
58 - .text-bottom {  
59 - width: 400rpx;  
60 - color: #999999;  
61 - font-size: 13px;  
62 - }  
63 - }  
64 - }  
65 - .item-right {  
66 - .text {  
67 - margin-right: 30rpx;  
68 - color: #999999;  
69 - font-size: 13px;  
70 - }  
71 - }  
72 - }  
73 - }  
74 -} 1 +.notify-page {
  2 + min-height: 100vh;
  3 + background-color: #f8f9fa;
  4 + padding-top: 10rpx;
  5 + padding-left: 27rpx;
  6 + .notify-page-top-select{
  7 + width: 750rpx;
  8 + height:53rpx;
  9 + background-color: #f8f9fa;
  10 + position:fixed;
  11 + top:0;
  12 + z-index: 99999;
  13 + }
  14 + .top-select{
  15 + background-color: #f8f9fa;
  16 + width: 700rpx;
  17 + position: relative;
  18 + top: 35rpx;
  19 + }
  20 +}
  21 +/deep/.u-list-item {
  22 + background: #ffffff;
  23 + width: 695rpx;
  24 + border-radius: 10px;
  25 + margin-top: 20rpx;
  26 +}
  27 +.notify-main {
  28 + .main {
  29 + flex-direction: column;
  30 + margin-left: -22rpx;
  31 + .main-item {
  32 + justify-content: space-between;
  33 + flex-direction: row;
  34 + margin: 15rpx;
  35 + width: 700rpx;
  36 + height: 136rpx;
  37 + background: #ffffff;
  38 + border-radius: 10px;
  39 + .item {
  40 + justify-content: space-between;
  41 + flex-direction: row;
  42 + .item-avatar {
  43 + margin-left: 30rpx;
  44 + .avatar {
  45 + margin-left: 30rpx;
  46 + }
  47 + }
  48 + .item-content {
  49 + margin-left: 30rpx;
  50 + text-align: left;
  51 + justify-content: space-between;
  52 + flex-direction: column;
  53 + .text-top {
  54 + width: 400rpx;
  55 + color: #2e384d;
  56 + font-size: 15px;
  57 + }
  58 + .text-bottom {
  59 + width: 400rpx;
  60 + color: #999999;
  61 + font-size: 13px;
  62 + }
  63 + }
  64 + }
  65 + .item-right {
  66 + .text {
  67 + margin-right: 30rpx;
  68 + color: #999999;
  69 + font-size: 13px;
  70 + }
  71 + }
  72 + }
  73 + }
  74 +}
sysnotify-subpackage/sys-notify/system-notify.vue renamed from sysNotifySubPage/sysNotifyPage/systemNotify.vue
1 -<template>  
2 - <view class="notify-page">  
3 - <!-- 公共组件-每个页面必须引入 -->  
4 - <public-module></public-module>  
5 - <view class="notify-page-top-select">  
6 - <view @click="handleTypeClick" class="top-select">  
7 - <u--input suffixIcon="arrow-down" shape="circle" disabled v-model="notifyType"  
8 - placeholder="请选择类型" border="surround"></u--input>  
9 - <u-action-sheet safe-area-inset-bottom :show="showType" :actions="actions" title="请选择类型" @close="showType = false"  
10 - @select="handleTypeSelect"></u-action-sheet>  
11 - </view>  
12 - </view>  
13 - <view style="height: 110rpx;"></view>  
14 - <view class="notify-main">  
15 - <mescroll-body ref="mescrollRef" @init="mescrollInit" :down="downOption" @down="downCallback"  
16 - @up="upCallback">  
17 - <view class="u-flex main">  
18 - <view @click="handleNotifyDetail(item, index)" class="u-flex main-item" v-for="(item, index) in list"  
19 - :key="index">  
20 - <view class="u-flex item">  
21 - <view class="item-avatar">  
22 - <u-avatar class="avatar" shape="circle" size="40" :src="item.sysNotice.avatar">  
23 - </u-avatar>  
24 - </view>  
25 - <view class="u-flex item-content">  
26 - <text class="text-top text-clip">{{ item.sysNotice.title }}</text>  
27 - <text class="text-bottom text-clip">{{ item.sysNotice.senderDate }}</text>  
28 - </view>  
29 - </view>  
30 - <view class="item-right u-flex" style="justify-content: space-between;">  
31 - <text class="text">{{ formatType(item.sysNotice.type) }}</text>  
32 - <!-- readStatus===0则阅读状态表示未阅读 -->  
33 - <u-badge style="margin-right: 10rpx;" v-if="item.readStatus == '0'" numberType="overflow"  
34 - isDot />  
35 - </view>  
36 - </view>  
37 - </view>  
38 - </mescroll-body>  
39 - </view>  
40 - </view>  
41 -</template>  
42 -  
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 { useNavigateTo } from '@/plugins/utils.js'  
47 -  
48 - export default {  
49 - mixins: [MescrollMixin],  
50 - data() {  
51 - return {  
52 - notifyType:'',  
53 - showType: false,  
54 - actions: [{  
55 - name: '全部',  
56 - value: ''  
57 - },  
58 - {  
59 - name: '会议',  
60 - value: 'MEETING'  
61 - },  
62 - {  
63 - name: '公告',  
64 - value: 'NOTICE'  
65 - },  
66 - {  
67 - name: '其他',  
68 - value: 'OTHER'  
69 - }  
70 - ],  
71 - page: {  
72 - page: 0,  
73 - pageSize: 10  
74 - },  
75 - downOption: {  
76 - auto: false //是否在初始化后,自动执行downCallback; 默认true  
77 - },  
78 - list: [],  
79 - selectType: '',  
80 - distance: 0,  
81 - };  
82 - },  
83 - onLoad(e) {  
84 - // 隐藏原生的tabbar  
85 - uni.hideTabBar();  
86 - },  
87 - methods: {  
88 - formatType(e) {  
89 - return this.actions.find((item)=>item.value===e && item.value!=='').name  
90 - },  
91 - handleTypeClick() {  
92 - this.showType = true;  
93 - uni.hideKeyboard();  
94 - },  
95 - handleTypeSelect(e) {  
96 - this.selectType = e.value;  
97 - this.page.page = 1;  
98 - this.notifyType = e.name;  
99 - this.loadData(1, !this.selectType ? null : this.selectType);  
100 - },  
101 - downCallback() {  
102 - this.list.length = 0;  
103 - this.page.page = 1;  
104 - this.loadData(1);  
105 - this.selectType = '';  
106 - this.notifyType = '';  
107 - },  
108 - upCallback() {  
109 - if (this.selectType) {  
110 - this.page.page += 1;  
111 - this.loadData(this.page.page, this.selectType);  
112 - } else {  
113 - this.page.page += 1;  
114 - this.loadData(this.page.page);  
115 - }  
116 - },  
117 - async loadData(page, type) {  
118 - let params = {  
119 - page,  
120 - pageSize: 10,  
121 - type  
122 - };  
123 - const res = await api.notifyApi.getNotifyApi({  
124 - params,  
125 - custom: {  
126 - load: false  
127 - }  
128 - })  
129 - uni.stopPullDownRefresh();  
130 - this.mescroll.endByPage(res.items.length, res.total);  
131 - if (page == 1) {  
132 - this.list = res.items;  
133 - } else {  
134 - this.list = this.list.concat(res.items);  
135 - }  
136 - },  
137 - async handleNotifyDetail(item, index) {  
138 - this.list[index].readStatus = 1;//点击了则说明阅读了  
139 - await api.notifyApi.byNotifyIdGetDetailApi(item.sysNotice.id)  
140 - useNavigateTo('./notifyDetail?data=', item.sysNotice)  
141 - }  
142 - }  
143 - };  
144 -</script>  
145 -  
146 -<style lang="scss" scoped>  
147 - @import './static/systemNotify.scss';  
148 -  
149 - /deep/ .u-badge--error {  
150 - position: relative;  
151 - right: 11rpx;  
152 - }  
153 -</style> 1 +<template>
  2 + <view class="notify-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <view class="notify-page-top-select">
  6 + <view @click="handleTypeClick" class="top-select">
  7 + <u--input suffixIcon="arrow-down" shape="circle" disabled v-model="notifyType"
  8 + placeholder="请选择类型" border="surround"></u--input>
  9 + <u-action-sheet safe-area-inset-bottom :show="showType" :actions="actions" title="请选择类型" @close="showType = false"
  10 + @select="handleTypeSelect"></u-action-sheet>
  11 + </view>
  12 + </view>
  13 + <view style="height: 110rpx;"></view>
  14 + <view class="notify-main">
  15 + <mescroll-body ref="mescrollRef" @init="mescrollInit" :down="downOption" @down="downCallback"
  16 + @up="upCallback">
  17 + <view class="u-flex main">
  18 + <view @click="handleNotifyDetail(item, index)" class="u-flex main-item" v-for="(item, index) in list"
  19 + :key="index">
  20 + <view class="u-flex item">
  21 + <view class="item-avatar">
  22 + <u-avatar class="avatar" shape="circle" size="40" :src="item.sysNotice.avatar">
  23 + </u-avatar>
  24 + </view>
  25 + <view class="u-flex item-content">
  26 + <text class="text-top text-clip">{{ item.sysNotice.title }}</text>
  27 + <text class="text-bottom text-clip">{{ item.sysNotice.senderDate }}</text>
  28 + </view>
  29 + </view>
  30 + <view class="item-right u-flex" style="justify-content: space-between;">
  31 + <text class="text">{{ formatType(item.sysNotice.type) }}</text>
  32 + <!-- readStatus===0则阅读状态表示未阅读 -->
  33 + <u-badge style="margin-right: 10rpx;" v-if="item.readStatus == '0'" numberType="overflow"
  34 + isDot />
  35 + </view>
  36 + </view>
  37 + </view>
  38 + </mescroll-body>
  39 + </view>
  40 + </view>
  41 +</template>
  42 +
  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 { useNavigateTo } from '@/plugins/utils.js'
  47 +
  48 + export default {
  49 + mixins: [MescrollMixin],
  50 + data() {
  51 + return {
  52 + notifyType:'',
  53 + showType: false,
  54 + actions: [{
  55 + name: '全部',
  56 + value: ''
  57 + },
  58 + {
  59 + name: '会议',
  60 + value: 'MEETING'
  61 + },
  62 + {
  63 + name: '公告',
  64 + value: 'NOTICE'
  65 + },
  66 + {
  67 + name: '其他',
  68 + value: 'OTHER'
  69 + }
  70 + ],
  71 + page: {
  72 + page: 0,
  73 + pageSize: 10
  74 + },
  75 + downOption: {
  76 + auto: false //是否在初始化后,自动执行downCallback; 默认true
  77 + },
  78 + list: [],
  79 + selectType: '',
  80 + distance: 0,
  81 + };
  82 + },
  83 + onLoad(e) {
  84 + // 隐藏原生的tabbar
  85 + uni.hideTabBar();
  86 + },
  87 + methods: {
  88 + formatType(e) {
  89 + return this.actions.find((item)=>item.value===e && item.value!=='').name
  90 + },
  91 + handleTypeClick() {
  92 + this.showType = true;
  93 + uni.hideKeyboard();
  94 + },
  95 + handleTypeSelect(e) {
  96 + this.selectType = e.value;
  97 + this.page.page = 1;
  98 + this.notifyType = e.name;
  99 + this.loadData(1, !this.selectType ? null : this.selectType);
  100 + },
  101 + downCallback() {
  102 + this.list.length = 0;
  103 + this.page.page = 1;
  104 + this.loadData(1);
  105 + this.selectType = '';
  106 + this.notifyType = '';
  107 + },
  108 + upCallback() {
  109 + if (this.selectType) {
  110 + this.page.page += 1;
  111 + this.loadData(this.page.page, this.selectType);
  112 + } else {
  113 + this.page.page += 1;
  114 + this.loadData(this.page.page);
  115 + }
  116 + },
  117 + async loadData(page, type) {
  118 + let params = {
  119 + page,
  120 + pageSize: 10,
  121 + type
  122 + };
  123 + const res = await api.notifyApi.getNotifyApi({
  124 + params,
  125 + custom: {
  126 + load: false
  127 + }
  128 + })
  129 + uni.stopPullDownRefresh();
  130 + this.mescroll.endByPage(res.items.length, res.total);
  131 + if (page == 1) {
  132 + this.list = res.items;
  133 + } else {
  134 + this.list = this.list.concat(res.items);
  135 + }
  136 + },
  137 + async handleNotifyDetail(item, index) {
  138 + this.list[index].readStatus = 1;//点击了则说明阅读了
  139 + await api.notifyApi.byNotifyIdGetDetailApi(item.sysNotice.id)
  140 + useNavigateTo('./notify-detail?data=', item.sysNotice)
  141 + }
  142 + }
  143 + };
  144 +</script>
  145 +
  146 +<style lang="scss" scoped>
  147 + @import './static/systemNotify.scss';
  148 +
  149 + /deep/ .u-badge--error {
  150 + position: relative;
  151 + right: 11rpx;
  152 + }
  153 +</style>