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 266 </style>
\ No newline at end of file
... ...
alarm-subpackage/alarm-detail/static/alarmDetail.scss renamed from alarmSubPage/alarmDetailPage/static/alarmDetail.scss
... ... @@ -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 34 export default {
26 35 getDeviceApi,
27 36 getDeviceDetail,
28   - getAttribute
29   -}
  37 + getAttribute,
  38 + issueCommand,
  39 + getRpcRecord
  40 +}
\ No newline at end of file
... ...
... ... @@ -103,11 +103,11 @@ uni.$u.http.interceptors.response.use(
103 103 const routers = getCurrentPages();
104 104 const currentRoute = routers[routers.length - 1].route;
105 105 const isLoginPage = currentRoute.includes(
106   - "publicLoginSubPage/public/login"
  106 + "login-subpackage/public/login"
107 107 );
108 108 !isLoginPage &&
109 109 uni.reLaunch({
110   - url: "/publicLoginSubPage/public/login",
  110 + url: "/login-subpackage/public/login",
111 111 });
112 112 // 清空登录信息
113 113 store.commit("emptyUserInfo");
... ...
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>
\ No newline at end of file
... ...
  1 +<template>
  2 +</template>
  3 +
  4 +<script>
  5 + export default {
  6 +
  7 + }
  8 +</script>
  9 +<style>
  10 +</style>
\ No newline at end of file
... ...
device-subpackage/device-detail/components/basic-info.vue renamed from deviceSubPage/deviceDetailPage/tabDetail/basicInfo.vue
1   -<template>
2   - <view class="basic-page">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module />
5   - <view class="basic-title">
6   - <view class="u-flex">
7   - <view style="padding-left: 32rpx;">
8   - <u-icon v-if="deviceDetail.deviceInfo.longitude !== ''" @click="handleClick" name="map-fill">
9   - </u-icon>
10   - </view>
11   - <view class="text-clip" style="margin-left: 20rpx;width:370rpx">
12   - {{ deviceDetail.alias? deviceDetail.alias: deviceDetail.name }}
13   - </view>
14   - <view style="margin-left: 20rpx; font-size: 14px;"
15   - :style="{ color: deviceDetail.deviceState === 'INACTIVE' ? '#666' : deviceDetail.deviceState === 'ONLINE' ? '#377DFF' : '#DE4437' }">
16   - {{ deviceStatus }}
17   - </view>
18   - </view>
19   - <view style="margin-right: 20rpx;"
20   - v-if="deviceDetail.deviceState === 'ONLINE' && deviceDetail.deviceType !== 'SENSOR'">
21   - <!-- #ifdef MP -->
22   - <u-button type="primary" shape="circle" size="mini" text="下发命令" @click="showModalBtn" />
23   - <!-- #endif -->
24   - <!-- #ifdef APP-PLUS -->
25   - <view class="cu-item" @tap="showModal" data-target="Modal">
26   - <text>下发命令</text>
27   - </view>
28   - <!-- #endif -->
29   - </view>
30   - </view>
31   - <view class="detail">
32   - <view class="detail-item">
33   - <view class="detail-label">设备编号</view>
34   - <view class="detail-value">{{ deviceDetail.sn }}</view>
35   - </view>
36   - <u-line length="90%" margin="0 auto"></u-line>
37   - <view class="detail-item">
38   - <view class="detail-label">设备类型</view>
39   - <view class="detail-value">{{ deviceType }}</view>
40   - </view>
41   - <u-line length="90%" margin="0 auto"></u-line>
42   - <view class="detail-item">
43   - <view class="detail-label">所属组织</view>
44   - <view class="detail-value">{{ deviceDetail.organizationDTO.name }}</view>
45   - </view>
46   - <u-line length="90%" margin="0 auto"></u-line>
47   - <view class="detail-item">
48   - <view class="detail-label">最后连接时间</view>
49   - <view class="detail-value">{{ formatLastOnlineTime }}</view>
50   - </view>
51   - <u-line length="90%" margin="0 auto"></u-line>
52   - <view class="detail-item">
53   - <view class="detail-label">是否告警</view>
54   - <view class="detail-value">{{ alarmStatus }}</view>
55   - </view>
56   - <u-line length="90%" margin="0 auto"></u-line>
57   - <view class="detail-item">
58   - <view class="detail-label">设备描述</view>
59   - <view class="detail-value">{{ deviceDetail.description }}</view>
60   - </view>
61   - </view>
62   - <!-- 下发命令 -->
63   - <!-- #ifdef APP-PLUS -->
64   - <view v-show="showNodal" class="cu-modal" :class="modalName=='Modal'?'show':''">
65   - <view class="cu-dialog" style="">
66   - <view class="modal_main">
67   - <view class='nav-list margin-top'>
68   - <view style="width: 100%; padding: 0 30rpx;">
69   - <view style="text-align: center; font-weight:700;margin-top: 40rpx;">命令下发</view>
70   - <view style="height: 20rpx;"></view>
71   - <view class="u-flex">
72   - <text
73   - style="color: #333; font-size: 14px;font-weight:700;margin-right: 30rpx;">下发类型:</text>
74   - <view>
75   - <radio-group style="display: flex;" @change="radioChange">
76   - <label class="uni-list-cell uni-list-cell-pd" v-for="(item, index) in items"
77   - :key="item.value">
78   - <view style="display: flex">
79   - <view style="margin-left: 10rpx;">
80   - <radio :value="item.value" :checked="index === current" />
81   - </view>
82   - <view style="width:10rpx"></view>
83   - <view style="margin-left: 10rpx;">{{item.name}}</view>
84   - </view>
85   - </label>
86   - </radio-group>
87   - </view>
88   - </view>
89   - <view style="margin-top: 15rpx">
90   - <view class="cusAppplusContent">
91   - <textarea v-model="inputCommandVal" placeholder="请输入下发内容(json格式)" />
92   - </view>
93   - </view>
94   - <view class="button-group">
95   - <view>
96   - <view class="cusAppplusCancelBtn" @click="cancelCommand"><text
97   - style="color: #333333">取消</text>
98   - </view>
99   - </view>
100   - <view>
101   - <view class="cusAppplusConfrimBtn" @click="confirmCommand">
102   - <text style="color:white">确认</text>
103   - </view>
104   - </view>
105   - </view>
106   - <view style="height:30rpx"></view>
107   - </view>
108   - </view>
109   - </view>
110   - </view>
111   - </view>
112   - <!-- #endif -->
113   - <!-- #ifdef MP -->
114   - <u-modal :show="showModel" closeOnClickOverlay :showConfirmButton="false" width="720rpx" @close="hiddenModal"
115   - @touchmove.stop.prevent="disabledScroll">
116   - <view style="width: 100%; padding: 0 30rpx;">
117   - <view style="text-align: center; font-weight:700;margin-bottom: 40rpx;">命令下发</view>
118   - <view class="u-flex">
119   - <text style="color: #333; font-size: 14px;font-weight:700;margin-right: 30rpx;">下发类型:</text>
120   -
121   - <u-radio-group v-model="commandType" placement="row">
122   - <u-radio activeColor="#3388FF" label="单向" name="OneWay"></u-radio>
123   - <view style="margin: 0 20rpx;"></view>
124   - <u-radio activeColor="#3388FF" label="双向" name="TwoWay"></u-radio>
125   - </u-radio-group>
126   - </view>
127   - <view style="margin-top: 28rpx;width: 100%;">
128   - <div class="u-flex u-row-between">
129   - <u--textarea placeholder="请输入下发内容(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>
\ No newline at end of file
... ...
device-subpackage/device-detail/components/command-detail.vue renamed from deviceSubPage/deviceDetailPage/tabDetail/CommandDetail.vue
1   -<template>
2   - <view class="command-detail">
3   - <view class="detail-top">{{ commandDetail.deviceName }}</view>
4   - <view class="detail">
5   - <view class="detail-item">
6   - <view class="detail-label">设备类型</view>
7   - <view class="detail-value">{{ deviceType }}</view>
8   - </view>
9   - <u-line length="90%" margin="0 auto"></u-line>
10   - <view class="detail-item">
11   - <view class="detail-label">设备编号</view>
12   - <view class="detail-value">{{ commandDetail.deviceSn }}</view>
13   - </view>
14   - <u-line length="90%" margin="0 auto"></u-line>
15   - <view class="detail-item">
16   - <view class="detail-label">所属组织</view>
17   - <view class="detail-value">{{ commandDetail.organizationName }}</view>
18   - </view>
19   - <u-line length="90%" margin="0 auto"></u-line>
20   - <view class="detail-item">
21   - <view class="detail-label">命令下发时间</view>
22   - <view class="detail-value">{{ format(commandDetail.createTime) }}</view>
23   - </view>
24   - <u-line length="90%" margin="0 auto"></u-line>
25   - <view class="detail-item">
26   - <view class="detail-label">命令类型</view>
27   - <view class="detail-value">{{ commandDetail.additionalInfo.cmdType===1?'服务':'自定义' }}</view>
28   - </view>
29   - <u-line length="90%" margin="0 auto" v-if="commandDetail.additionalInfo.cmdType"></u-line>
30   - <view class="detail-item">
31   - <view class="detail-label">响应类型</view>
32   - <view class="detail-value">{{ commandDetail.request.oneway ? '单向' : '双向' }}</view>
33   - </view>
34   - <u-line length="90%" margin="0 auto"></u-line>
35   - <view class="detail-item">
36   - <view class="detail-label">命令状态</view>
37   - <view class="detail-value">{{ commandDetail.statusName }}</view>
38   - </view>
39   - <u-line length="90%" margin="0 auto"></u-line>
40   - <view class="detail-item" v-if="!commandDetail.request.oneway">
41   - <view class="detail-label">响应结果</view>
42   - <view class="detail-value">{{ commandDetail.response?JSON.stringify(commandDetail.response):'无' }}</view>
43   - </view>
44   - <!-- <view class="detail-item" v-if="!commandDetail.request.oneway">
45   - <view class="detail-label">响应失败内容</view>
46   - <view class="detail-value" style="width: 400rpx;" v-if="commandDetail.response.status!=='SUCCESS'">
47   - <u--textarea placeholder="响应失败内容" v-model="failContent" />
48   - </view>
49   - </view> -->
50   - </view>
51   - <view class="command">命令内容</view>
52   - <u-textarea :value="formatValue(commandDetail.request.body)" disabled></u-textarea>
53   - <view style="height: 50rpx;"></view>
54   - </view>
55   -</template>
56   -
57   -<script>
58   - import {
59   - formatToDate
60   - } from '@/plugins/utils.js';
61   - export default {
62   - data() {
63   - return {
64   - commandDetail: {},
65   - failContent: ""
66   - };
67   - },
68   - computed: {
69   - deviceType() {
70   - return this.commandDetail.deviceType === 'DIRECT_CONNECTION' ?
71   - '直连设备' :
72   - this.commandDetail.deviceType === 'GATEWAY' ?
73   - '网关设备' :
74   - this.commandDetail.deviceType === 'SENSOR' ?
75   - '网关子设备' :
76   - '';
77   - }
78   - },
79   - methods: {
80   - format(date) {
81   - return formatToDate(date, 'YYYY-MM-DD HH:mm:ss');
82   - },
83   - formatValue(value) {
84   - try {
85   - const val = JSON.parse(value['params']);
86   - //微信小程序端object无法显示,格式化为字符串
87   - const stringifyVal = JSON.stringify(val['params'])
88   - const formatVal = stringifyVal
89   - .replace(/\\"/g, '"')
90   - .replace(/]"/g, ']')
91   - .replace(/"\[/g, '[');
92   - return formatVal
93   - } catch (e) {
94   - console.error("命令记录页面格式化无返回值", e);
95   - return value['params']
96   - }
97   - }
98   - },
99   - onLoad(options) {
100   - const {
101   - data
102   - } = options;
103   - this.commandDetail = JSON.parse(data);
104   - if (this.commandDetail.response.status === 'SUCCESS') return
105   - this.failContent = JSON.stringify(this.commandDetail.response.error)
106   - }
107   - };
108   -</script>
109   -
110   -<style lang="scss" scoped>
111   - .command-detail {
112   - padding: 0 30rpx;
113   - height: 100vh;
114   - background-color: #f8f9fa;
115   -
116   - .detail-top {
117   - height: 118rpx;
118   - width: 690rpx;
119   - display: flex;
120   - align-items: center;
121   - background-color: #fff;
122   - color: #333;
123   - border-radius: 20rpx;
124   - font-size: 15px;
125   - margin-top: 30rpx;
126   - padding: 30rpx;
127   - }
128   -
129   - .detail {
130   - background-color: #fff;
131   - margin-top: 30rpx;
132   - border-radius: 20rpx;
133   - width: 690rpx;
134   -
135   - .detail-item {
136   - padding: 30rpx;
137   - display: flex;
138   - align-items: center;
139   -
140   - .detail-label {
141   - color: #333;
142   - font-size: 15px;
143   - }
144   -
145   - .detail-value {
146   - color: #666;
147   - font-size: 14px;
148   - margin-left: 30rpx;
149   - }
150   - }
151   - }
152   -
153   - .command {
154   - margin: 30rpx 0;
155   - }
156   - }
157   -</style>
  1 +<template>
  2 + <view class="command-detail">
  3 + <view class="detail-top">{{ commandDetail.deviceName }}</view>
  4 + <view class="detail">
  5 + <view class="detail-item">
  6 + <view class="detail-label">设备类型</view>
  7 + <view class="detail-value">{{ deviceType }}</view>
  8 + </view>
  9 + <u-line length="90%" margin="0 auto"></u-line>
  10 + <view class="detail-item">
  11 + <view class="detail-label">设备编号</view>
  12 + <view class="detail-value">{{ commandDetail.deviceSn }}</view>
  13 + </view>
  14 + <u-line length="90%" margin="0 auto"></u-line>
  15 + <view class="detail-item">
  16 + <view class="detail-label">所属组织</view>
  17 + <view class="detail-value">{{ commandDetail.organizationName }}</view>
  18 + </view>
  19 + <u-line length="90%" margin="0 auto"></u-line>
  20 + <view class="detail-item">
  21 + <view class="detail-label">命令下发时间</view>
  22 + <view class="detail-value">{{ format(commandDetail.createTime) }}</view>
  23 + </view>
  24 + <u-line length="90%" margin="0 auto"></u-line>
  25 + <view class="detail-item">
  26 + <view class="detail-label">命令类型</view>
  27 + <view class="detail-value">{{ commandDetail.additionalInfo.cmdType===1?'服务':'自定义' }}</view>
  28 + </view>
  29 + <u-line length="90%" margin="0 auto" v-if="commandDetail.additionalInfo.cmdType"></u-line>
  30 + <view class="detail-item">
  31 + <view class="detail-label">响应类型</view>
  32 + <view class="detail-value">{{ commandDetail.request.oneway ? '单向' : '双向' }}</view>
  33 + </view>
  34 + <u-line length="90%" margin="0 auto"></u-line>
  35 + <view class="detail-item">
  36 + <view class="detail-label">命令状态</view>
  37 + <view class="detail-value">{{ commandDetail.statusName }}</view>
  38 + </view>
  39 + <u-line length="90%" margin="0 auto"></u-line>
  40 + <view class="detail-item" v-if="!commandDetail.request.oneway">
  41 + <view class="detail-label">响应结果</view>
  42 + <view class="detail-value">{{ commandDetail.response?JSON.stringify(commandDetail.response):'无' }}
  43 + </view>
  44 + </view>
  45 + </view>
  46 + <view class="command">命令内容</view>
  47 + <u-textarea :value="formatValue(commandDetail.request.body)" disabled></u-textarea>
  48 + <view style="height: 50rpx;"></view>
  49 + </view>
  50 +</template>
  51 +
  52 +<script>
  53 + import {
  54 + formatToDate
  55 + } from '@/plugins/utils.js';
  56 + export default {
  57 + data() {
  58 + return {
  59 + commandDetail: {},
  60 + failContent: ""
  61 + };
  62 + },
  63 + computed: {
  64 + deviceType() {
  65 + return this.commandDetail.deviceType === 'DIRECT_CONNECTION' ?
  66 + '直连设备' :
  67 + this.commandDetail.deviceType === 'GATEWAY' ?
  68 + '网关设备' :
  69 + this.commandDetail.deviceType === 'SENSOR' ?
  70 + '网关子设备' :
  71 + '';
  72 + }
  73 + },
  74 + methods: {
  75 + format(date) {
  76 + return formatToDate(date, 'YYYY-MM-DD HH:mm:ss');
  77 + },
  78 + formatValue(value) {
  79 + try {
  80 + const val = JSON.parse(value['params']);
  81 + //微信小程序端object无法显示,格式化为字符串
  82 + const stringifyVal = JSON.stringify(val['params'])
  83 + const formatVal = stringifyVal
  84 + .replace(/\\"/g, '"')
  85 + .replace(/]"/g, ']')
  86 + .replace(/"\[/g, '[');
  87 + return formatVal
  88 + } catch (e) {
  89 + return value['params']
  90 + }
  91 + }
  92 + },
  93 + onLoad(options) {
  94 + const {
  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>
\ No newline at end of file
... ...
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>
\ No newline at end of file
... ...
device-subpackage/device-detail/components/history-data.vue renamed from deviceSubPage/deviceDetailPage/tabDetail/historyData.vue
1   -<template>
2   - <view class="historyData">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <view class="historyData-top">
6   - <u-form :label-style="{ 'font-size': '0rpx' }">
7   - <u-form-item @click="openCalendar">
8   - <u-input v-model="timeData.selectTime" disabled disabledColor="#fff" placeholder="请选择日期"
9   - border="none" suffixIcon="arrow-down">
10   - <template slot="prefix">
11   - <image class="icon" src="../../../static/can-der.png"></image>
12   - </template>
13   - </u-input>
14   - </u-form-item>
15   - <u-form-item @click="openTimeGap">
16   - <u-input v-model="timeData.getTimeGap" disabled disabledColor="#fff" placeholder="请选择时间区间"
17   - border="none" suffixIcon="arrow-down">
18   - <template slot="prefix">
19   - <image class="icon" src="../../../static/time.png"></image>
20   - </template>
21   - </u-input>
22   - </u-form-item>
23   - <u-form-item @click="openAvg">
24   - <u-input shape="circle" v-model="aggText" placeholder="请选择数据聚合功能" disabled disabledColor="#377DFF0D"
25   - suffixIcon="arrow-down" />
26   - </u-form-item>
27   - <u-form-item @click="openTimeGap" v-if="limitFlag">
28   - <view class="u-flex">
29   - <text>最大条数</text>
30   - <u-number-box style="margin-left:30rpx" class="ml-10" v-model="timeData.limit" :min="7"
31   - :max="50000"></u-number-box>
32   - </view>
33   - </u-form-item>
34   - <u-form-item @click="openType">
35   - <u-input shape="circle" v-model="timeData.getType" placeholder="请选择属性" disabled
36   - disabledColor="#377DFF0D" suffixIcon="arrow-down" />
37   - </u-form-item>
38   - </u-form>
39   -
40   - <view class="charts-box" v-show="historyData.length">
41   - <qiun-data-charts type="area" canvas2d canvasId="daskujdhasljkdcnzjkdfhuoqwlqwjhkdsamjczxnmdasd123321"
42   - :chartData="chartData" :ontouch="true"
43   - :opts="{ xAxis: { disabled: true, itemCount: 6, scrollShow: true }, legend: { show: false }, enableScroll: true }" />
44   - </view>
45   - <mescroll-empty v-if="!historyData.length" />
46   - </view>
47   - <view class="historyData-bottom" v-show="historyData.length">
48   - <view class="table">
49   - <view class="tr bg-w" v-if="historyData.length">
50   - <view class="th">变量值</view>
51   - <view class="th">更新时间</view>
52   - </view>
53   - <view class="tr bg-g" :class="{ odd: index % 2 === 0 }" v-for="(item, index) in historyData"
54   - :key="index">
55   - <view class="td">{{ item.value }}</view>
56   - <view class="td">{{ item.ts }}</view>
57   - </view>
58   - </view>
59   - </view>
60   - <u-calendar :show="showCalendar" :defaultDate="defaultDate" closeOnClickOverlay mode="range" startText="开始时间"
61   - endText="结束时间" confirmDisabledText="请选择日期" :minDate="minDate" :maxDate="maxDate" @confirm="calendarConfirm"
62   - @close="calendarClose"></u-calendar>
63   - <u-picker :show="showTimeGap" :columns="columns" keyName="label" closeOnClickOverlay @confirm="confirmTimeGap"
64   - @cancel="cancelTimeGap" @close="cancelTimeGap" :defaultIndex="[3]"></u-picker>
65   - <u-picker :show="showSelectType" :columns="keys" closeOnClickOverlay @confirm="confirmTypeGap"
66   - @cancel="cancelTypeGap" @close="cancelTypeGap"></u-picker>
67   - <u-picker :show="showSelectAvg" :columns="avgColumns" keyName="label" closeOnClickOverlay
68   - @confirm="confirmAvgGap" @cancel="showSelectAvg=false" @close="showSelectAvg=false"></u-picker>
69   - </view>
70   -</template>
71   -
72   -<script>
73   - import fTabbar from '@/components/module/f-tabbar/f-tabbar';
74   - import qiunDataCharts from '@/uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue';
75   - import {
76   - getHistoryData
77   - } from '../api/index.js';
78   - import {
79   - formatToDate
80   - } from '@/plugins/utils.js';
81   - const d = new Date();
82   - const year = d.getFullYear();
83   - let month = d.getMonth() + 1;
84   - month = month < 10 ? `0${month}` : month;
85   - const date = d.getDate();
86   - export default {
87   - components: {
88   - fTabbar,
89   - qiunDataCharts
90   - },
91   - props: {
92   - keys: {
93   - type: Array,
94   - default: () => []
95   - },
96   - yesterday: {
97   - type: String,
98   - default: ''
99   - },
100   - today: {
101   - type: String,
102   - default: ''
103   - },
104   - timeDiff: {
105   - type: String,
106   - default: ''
107   - },
108   - historyData: {
109   - type: Array,
110   - default: () => []
111   - },
112   - entityId: {
113   - type: String,
114   - required: true
115   - },
116   - start: {
117   - type: String,
118   - required: true
119   - },
120   - end: {
121   - type: String,
122   - required: true
123   - }
124   - },
125   - data() {
126   - return {
127   - limitFlag: true,
128   - avgColumns: [
129   - [{
130   - label: '最小值',
131   - value: 'MIN'
132   - }, {
133   - label: '最大值',
134   - value: 'MAX'
135   - },
136   - {
137   - label: '平均值',
138   - value: 'AVG'
139   - },
140   - {
141   - label: '求和',
142   - value: 'SUM'
143   - },
144   - {
145   - label: '计数',
146   - value: 'COUNT'
147   - },
148   - {
149   - label: '空',
150   - value: 'NONE'
151   - },
152   - ]
153   - ],
154   - startTs: this.start,
155   - endTs: this.end,
156   - showCalendar: false,
157   - showTimeGap: false,
158   - showSelectType: false,
159   - showSelectAvg: false,
160   - minDate: `${year}-${month - 1}-${date}`,
161   - maxDate: `${year}-${month}-${date + 1}`,
162   - defaultDate: [this.yesterday, this.today],
163   - chartData: {
164   - categories: this.historyData.length && this.historyData.map(item => item.ts),
165   - series: [{
166   - name: this.keys[0][0],
167   - data: this.historyData.length && this.historyData.map(item => Number(item.value))
168   - }]
169   - },
170   - columns: [
171   - [{
172   - label: '5分钟',
173   - value: 300000
174   - },
175   - {
176   - label: '10分钟',
177   - value: 600000
178   - },
179   - {
180   - label: '15分钟',
181   - value: 900000
182   - },
183   - {
184   - label: '30分钟',
185   - value: 1800000
186   - },
187   - {
188   - label: '1小时',
189   - value: 3600000
190   - },
191   - {
192   - label: '2小时',
193   - value: 7200000
194   - }
195   - ]
196   - ],
197   - timeData: {
198   - selectTime: this.yesterday + ' 至 ' + this.today,
199   - getTimeGap: this.timeDiff,
200   - getType: this.keys[0][0],
201   - limit: 7,
202   - agg: 'NONE'
203   - },
204   - aggText: '空'
205   - };
206   - },
207   - watch: {
208   - historyData(newValue) {
209   - if (!newValue.length) {
210   - this.chartData.categories = [];
211   - this.chartData.series = [];
212   - } else {
213   - this.chartData.categories = newValue.map(item => item.ts);
214   - this.chartData.series = [{
215   - name: this.keys[0][0],
216   - data: newValue.map(item => Number(item.value))
217   - }];
218   - }
219   - }
220   - },
221   - methods: {
222   - // 动态生成Columns
223   - generateColumns(value) {
224   - if (value < 604800000) {
225   - // 小于7天
226   - return [
227   - [{
228   - label: '5分钟',
229   - value: 300000
230   - },
231   - {
232   - label: '10分钟',
233   - value: 600000
234   - },
235   - {
236   - label: '15分钟',
237   - value: 900000
238   - },
239   - {
240   - label: '30分钟',
241   - value: 1800000
242   - },
243   - {
244   - label: '1小时',
245   - value: 3600000
246   - },
247   - {
248   - label: '2小时',
249   - value: 7200000
250   - }
251   - ]
252   - ];
253   - } else if (value < 2592000000) {
254   - // 小于30天
255   - return [
256   - [{
257   - label: '30分钟',
258   - value: 1800000
259   - },
260   - {
261   - label: '1小时',
262   - value: 3600000
263   - },
264   - {
265   - label: '2小时',
266   - value: 7200000
267   - },
268   - {
269   - label: '5小时',
270   - value: 18000000
271   - },
272   - {
273   - label: '10小时',
274   - value: 36000000
275   - },
276   - {
277   - label: '12小时',
278   - value: 43200000
279   - },
280   - {
281   - label: '1天',
282   - value: 86400000
283   - }
284   - ]
285   - ];
286   - } else if (value >= 2592000000) {
287   - // 大于30天
288   - return [
289   - [{
290   - label: '2小时',
291   - value: 7200000
292   - },
293   - {
294   - label: '5小时',
295   - value: 18000000
296   - },
297   - {
298   - label: '10小时',
299   - value: 36000000
300   - },
301   - {
302   - label: '12小时',
303   - value: 43200000
304   - },
305   - {
306   - label: '1天',
307   - value: 86400000
308   - }
309   - ]
310   - ];
311   - }
312   - },
313   - openCalendar() {
314   - this.showCalendar = true;
315   - },
316   - openTimeGap() {
317   - this.showTimeGap = true;
318   - },
319   - openType() {
320   - this.showSelectType = true;
321   - },
322   - openAvg() {
323   - this.showSelectAvg = true
324   - },
325   - calendarConfirm(date) {
326   - this.showCalendar = false;
327   - this.timeData.selectTime = `${date[0]} 至 ${date[date.length - 1]}`;
328   - // 选择的日期时间差(时间戳)
329   - const timeDiff = formatToDate(date[date.length - 1], 'x') - formatToDate(date[0], 'x');
330   - const genColumns = this.generateColumns(timeDiff);
331   - this.columns = genColumns;
332   - this.timeData.getTimeGap = '';
333   - this.timeData.getType = '';
334   - this.startTs = formatToDate(date[0], 'x');
335   - // 最后时间的最后一秒
336   - this.endTs = formatToDate(`${date[date.length - 1]} 23:59:59`, 'x');
337   - },
338   - calendarClose() {
339   - this.showCalendar = false;
340   - },
341   - confirmTimeGap(time) {
342   - this.showTimeGap = false;
343   - this.timeData.getTimeGap = time.value[0].label;
344   - this.timeData.getType = '';
345   - },
346   -
347   - cancelTimeGap() {
348   - this.showTimeGap = false;
349   - },
350   - confirmAvgGap(e) {
351   - this.timeData.agg = e.value[0].value
352   - this.aggText = e.value[0].label
353   - if (e.value[0].value === 'NONE') {
354   - this.limitFlag = true
355   - this.timeData.limit = 7
356   - } else {
357   - this.timeData.limit = null
358   - this.limitFlag = false
359   - }
360   - this.showSelectAvg = false
361   - },
362   - async confirmTypeGap(time) {
363   - this.showSelectType = false;
364   - this.timeData.getType = time.value[0];
365   - const interval = this.columns[0].find(item => item.label === this.timeData.getTimeGap);
366   - const data = await getHistoryData({
367   - startTs: this.startTs,
368   - endTs: this.endTs,
369   - keys: this.timeData.getType,
370   - interval: this.limitFlag ? null : interval.value,
371   - entityId: this.entityId,
372   - limit: this.timeData.limit,
373   - agg: this.timeData.agg
374   - });
375   - this.$emit('update', data[this.timeData.getType]);
376   - },
377   - cancelTypeGap() {
378   - this.showSelectType = false;
379   - }
380   - }
381   - };
382   -</script>
383   -
384   -<style lang="scss" scoped>
385   - .charts-box {
386   - width: 100%;
387   - height: 550rpx;
388   - }
389   -
390   - .historyData {
391   - margin: 30rpx;
392   -
393   - .historyData-top {
394   - padding: 30rpx;
395   - background-color: #fff;
396   - // height: 870rpx;
397   - border-radius: 20rpx;
398   -
399   - .icon {
400   - width: 28rpx;
401   - height: 28rpx;
402   - margin-right: 15rpx;
403   - }
404   - }
405   -
406   - .historyData-bottom {
407   - margin-top: 30rpx;
408   - background-color: #fff;
409   - border-radius: 20rpx;
410   -
411   - .table {
412   - border: 0px solid darkgray;
413   -
414   - .tr {
415   - display: flex;
416   - width: 100%;
417   - justify-content: center;
418   - height: 3rem;
419   - align-items: center;
420   -
421   - .th {
422   - display: flex;
423   - justify-content: center;
424   - align-items: center;
425   - width: 50%;
426   - color: #333;
427   - font-weight: 500;
428   - }
429   -
430   - .td {
431   - color: #999;
432   - width: 50%;
433   - display: flex;
434   - justify-content: center;
435   - text-align: center;
436   - }
437   - }
438   - }
439   - }
440   - }
441   -
442   - .odd {
443   - background-color: #f9fcff;
444   - }
  1 +<template>
  2 + <view class="historyData">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <view class="historyData-top">
  6 + <u-form :label-style="{ 'font-size': '0rpx' }">
  7 + <u-form-item @click="openCalendar">
  8 + <u-input v-model="timeData.selectTime" disabled disabledColor="#fff" placeholder="请选择日期"
  9 + border="none" suffixIcon="arrow-down">
  10 + <template slot="prefix">
  11 + <image class="icon" src="../../../static/can-der.png"></image>
  12 + </template>
  13 + </u-input>
  14 + </u-form-item>
  15 + <u-form-item @click="openTimeGap">
  16 + <u-input v-model="timeData.getTimeGap" disabled disabledColor="#fff" placeholder="请选择时间区间"
  17 + border="none" suffixIcon="arrow-down">
  18 + <template slot="prefix">
  19 + <image class="icon" src="../../../static/time.png"></image>
  20 + </template>
  21 + </u-input>
  22 + </u-form-item>
  23 + <u-form-item @click="openAvg">
  24 + <u-input shape="circle" v-model="aggText" placeholder="请选择数据聚合功能" disabled disabledColor="#377DFF0D"
  25 + suffixIcon="arrow-down" />
  26 + </u-form-item>
  27 + <u-form-item @click="openTimeGap" v-if="limitFlag">
  28 + <view class="u-flex">
  29 + <text>最大条数</text>
  30 + <u-number-box style="margin-left:30rpx" class="ml-10" v-model="timeData.limit" :min="7"
  31 + :max="50000"></u-number-box>
  32 + </view>
  33 + </u-form-item>
  34 + <u-form-item @click="openType">
  35 + <u-input shape="circle" v-model="timeData.getType" placeholder="请选择属性" disabled
  36 + disabledColor="#377DFF0D" suffixIcon="arrow-down" />
  37 + </u-form-item>
  38 + </u-form>
  39 +
  40 + <view class="charts-box" v-show="historyData.length">
  41 + <qiun-data-charts type="area" canvas2d canvasId="daskujdhasljkdcnzjkdfhuoqwlqwjhkdsamjczxnmdasd123321"
  42 + :chartData="chartData" :ontouch="true"
  43 + :opts="{ xAxis: { disabled: true, itemCount: 6, scrollShow: true }, legend: { show: false }, enableScroll: true }" />
  44 + </view>
  45 + <mescroll-empty v-if="!historyData.length" />
  46 + </view>
  47 + <view class="historyData-bottom" v-show="historyData.length">
  48 + <view class="table">
  49 + <view class="tr bg-w" v-if="historyData.length">
  50 + <view class="th">变量值</view>
  51 + <view class="th">更新时间</view>
  52 + </view>
  53 + <view class="tr bg-g" :class="{ odd: index % 2 === 0 }" v-for="(item, index) in historyData"
  54 + :key="index">
  55 + <view class="td">{{ item.value }}</view>
  56 + <view class="td">{{ item.ts }}</view>
  57 + </view>
  58 + </view>
  59 + </view>
  60 + <u-calendar :show="showCalendar" :defaultDate="defaultDate" closeOnClickOverlay mode="range" startText="开始时间"
  61 + endText="结束时间" confirmDisabledText="请选择日期" :minDate="minDate" :maxDate="maxDate" @confirm="calendarConfirm"
  62 + @close="calendarClose"></u-calendar>
  63 + <u-picker :show="showTimeGap" :columns="columns" keyName="label" closeOnClickOverlay @confirm="confirmTimeGap"
  64 + @cancel="cancelTimeGap" @close="cancelTimeGap" :defaultIndex="[3]"></u-picker>
  65 + <u-picker :show="showSelectType" :columns="keys" closeOnClickOverlay @confirm="confirmTypeGap"
  66 + @cancel="cancelTypeGap" @close="cancelTypeGap"></u-picker>
  67 + <u-picker :show="showSelectAvg" :columns="avgColumns" keyName="label" closeOnClickOverlay
  68 + @confirm="confirmAvgGap" @cancel="showSelectAvg=false" @close="showSelectAvg=false"></u-picker>
  69 + </view>
  70 +</template>
  71 +
  72 +<script>
  73 + import fTabbar from '@/components/module/f-tabbar/f-tabbar';
  74 + import qiunDataCharts from '@/uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue';
  75 + import {
  76 + getHistoryData
  77 + } from '../api/index.js';
  78 + import {
  79 + formatToDate
  80 + } from '@/plugins/utils.js';
  81 + const d = new Date();
  82 + const year = d.getFullYear();
  83 + let month = d.getMonth() + 1;
  84 + month = month < 10 ? `0${month}` : month;
  85 + const date = d.getDate();
  86 + export default {
  87 + components: {
  88 + fTabbar,
  89 + qiunDataCharts
  90 + },
  91 + props: {
  92 + keys: {
  93 + type: Array,
  94 + default: () => []
  95 + },
  96 + yesterday: {
  97 + type: String,
  98 + default: ''
  99 + },
  100 + today: {
  101 + type: String,
  102 + default: ''
  103 + },
  104 + timeDiff: {
  105 + type: String,
  106 + default: ''
  107 + },
  108 + historyData: {
  109 + type: Array,
  110 + default: () => []
  111 + },
  112 + entityId: {
  113 + type: String,
  114 + required: true
  115 + },
  116 + start: {
  117 + type: String,
  118 + required: true
  119 + },
  120 + end: {
  121 + type: String,
  122 + required: true
  123 + }
  124 + },
  125 + data() {
  126 + return {
  127 + limitFlag: true,
  128 + avgColumns: [
  129 + [{
  130 + label: '最小值',
  131 + value: 'MIN'
  132 + }, {
  133 + label: '最大值',
  134 + value: 'MAX'
  135 + },
  136 + {
  137 + label: '平均值',
  138 + value: 'AVG'
  139 + },
  140 + {
  141 + label: '求和',
  142 + value: 'SUM'
  143 + },
  144 + {
  145 + label: '计数',
  146 + value: 'COUNT'
  147 + },
  148 + {
  149 + label: '空',
  150 + value: 'NONE'
  151 + },
  152 + ]
  153 + ],
  154 + startTs: this.start,
  155 + endTs: this.end,
  156 + showCalendar: false,
  157 + showTimeGap: false,
  158 + showSelectType: false,
  159 + showSelectAvg: false,
  160 + minDate: `${year}-${month - 1}-${date}`,
  161 + maxDate: `${year}-${month}-${date + 1}`,
  162 + defaultDate: [this.yesterday, this.today],
  163 + chartData: {
  164 + categories: this.historyData.length && this.historyData.map(item => item.ts),
  165 + series: [{
  166 + name: this.keys[0][0],
  167 + data: this.historyData.length && this.historyData.map(item => Number(item.value))
  168 + }]
  169 + },
  170 + columns: [
  171 + [{
  172 + label: '5分钟',
  173 + value: 300000
  174 + },
  175 + {
  176 + label: '10分钟',
  177 + value: 600000
  178 + },
  179 + {
  180 + label: '15分钟',
  181 + value: 900000
  182 + },
  183 + {
  184 + label: '30分钟',
  185 + value: 1800000
  186 + },
  187 + {
  188 + label: '1小时',
  189 + value: 3600000
  190 + },
  191 + {
  192 + label: '2小时',
  193 + value: 7200000
  194 + }
  195 + ]
  196 + ],
  197 + timeData: {
  198 + selectTime: this.yesterday + ' 至 ' + this.today,
  199 + getTimeGap: this.timeDiff,
  200 + getType: this.keys[0][0],
  201 + limit: 7,
  202 + agg: 'NONE'
  203 + },
  204 + aggText: '空'
  205 + };
  206 + },
  207 + watch: {
  208 + historyData(newValue) {
  209 + if (!newValue.length) {
  210 + this.chartData.categories = [];
  211 + this.chartData.series = [];
  212 + } else {
  213 + this.chartData.categories = newValue.map(item => item.ts);
  214 + this.chartData.series = [{
  215 + name: this.keys[0][0],
  216 + data: newValue.map(item => Number(item.value))
  217 + }];
  218 + }
  219 + }
  220 + },
  221 + methods: {
  222 + // 动态生成Columns
  223 + generateColumns(value) {
  224 + if (value < 604800000) {
  225 + // 小于7天
  226 + return [
  227 + [{
  228 + label: '5分钟',
  229 + value: 300000
  230 + },
  231 + {
  232 + label: '10分钟',
  233 + value: 600000
  234 + },
  235 + {
  236 + label: '15分钟',
  237 + value: 900000
  238 + },
  239 + {
  240 + label: '30分钟',
  241 + value: 1800000
  242 + },
  243 + {
  244 + label: '1小时',
  245 + value: 3600000
  246 + },
  247 + {
  248 + label: '2小时',
  249 + value: 7200000
  250 + }
  251 + ]
  252 + ];
  253 + } else if (value < 2592000000) {
  254 + // 小于30天
  255 + return [
  256 + [{
  257 + label: '30分钟',
  258 + value: 1800000
  259 + },
  260 + {
  261 + label: '1小时',
  262 + value: 3600000
  263 + },
  264 + {
  265 + label: '2小时',
  266 + value: 7200000
  267 + },
  268 + {
  269 + label: '5小时',
  270 + value: 18000000
  271 + },
  272 + {
  273 + label: '10小时',
  274 + value: 36000000
  275 + },
  276 + {
  277 + label: '12小时',
  278 + value: 43200000
  279 + },
  280 + {
  281 + label: '1天',
  282 + value: 86400000
  283 + }
  284 + ]
  285 + ];
  286 + } else if (value >= 2592000000) {
  287 + // 大于30天
  288 + return [
  289 + [{
  290 + label: '2小时',
  291 + value: 7200000
  292 + },
  293 + {
  294 + label: '5小时',
  295 + value: 18000000
  296 + },
  297 + {
  298 + label: '10小时',
  299 + value: 36000000
  300 + },
  301 + {
  302 + label: '12小时',
  303 + value: 43200000
  304 + },
  305 + {
  306 + label: '1天',
  307 + value: 86400000
  308 + }
  309 + ]
  310 + ];
  311 + }
  312 + },
  313 + openCalendar() {
  314 + this.showCalendar = true;
  315 + },
  316 + openTimeGap() {
  317 + this.showTimeGap = true;
  318 + },
  319 + openType() {
  320 + this.showSelectType = true;
  321 + },
  322 + openAvg() {
  323 + this.showSelectAvg = true
  324 + },
  325 + calendarConfirm(date) {
  326 + this.showCalendar = false;
  327 + this.timeData.selectTime = `${date[0]} 至 ${date[date.length - 1]}`;
  328 + // 选择的日期时间差(时间戳)
  329 + const timeDiff = formatToDate(date[date.length - 1], 'x') - formatToDate(date[0], 'x');
  330 + const genColumns = this.generateColumns(timeDiff);
  331 + this.columns = genColumns;
  332 + this.timeData.getTimeGap = '';
  333 + this.timeData.getType = '';
  334 + this.startTs = formatToDate(date[0], 'x');
  335 + // 最后时间的最后一秒
  336 + this.endTs = formatToDate(`${date[date.length - 1]} 23:59:59`, 'x');
  337 + },
  338 + calendarClose() {
  339 + this.showCalendar = false;
  340 + },
  341 + confirmTimeGap(time) {
  342 + this.showTimeGap = false;
  343 + this.timeData.getTimeGap = time.value[0].label;
  344 + this.timeData.getType = '';
  345 + },
  346 +
  347 + cancelTimeGap() {
  348 + this.showTimeGap = false;
  349 + },
  350 + confirmAvgGap(e) {
  351 + this.timeData.agg = e.value[0].value
  352 + this.aggText = e.value[0].label
  353 + if (e.value[0].value === 'NONE') {
  354 + this.limitFlag = true
  355 + this.timeData.limit = 7
  356 + } else {
  357 + this.timeData.limit = null
  358 + this.limitFlag = false
  359 + }
  360 + this.showSelectAvg = false
  361 + },
  362 + async confirmTypeGap(time) {
  363 + this.showSelectType = false;
  364 + this.timeData.getType = time.value[0];
  365 + const interval = this.columns[0].find(item => item.label === this.timeData.getTimeGap);
  366 + const data = await getHistoryData({
  367 + startTs: this.startTs,
  368 + endTs: this.endTs,
  369 + keys: this.timeData.getType,
  370 + interval: this.limitFlag ? null : interval.value,
  371 + entityId: this.entityId,
  372 + limit: this.timeData.limit,
  373 + agg: this.timeData.agg
  374 + });
  375 + this.$emit('update', data[this.timeData.getType]);
  376 + },
  377 + cancelTypeGap() {
  378 + this.showSelectType = false;
  379 + }
  380 + }
  381 + };
  382 +</script>
  383 +
  384 +<style lang="scss" scoped>
  385 + .charts-box {
  386 + width: 100%;
  387 + height: 550rpx;
  388 + }
  389 +
  390 + .historyData {
  391 + margin: 30rpx;
  392 +
  393 + .historyData-top {
  394 + padding: 30rpx;
  395 + background-color: #fff;
  396 + // height: 870rpx;
  397 + border-radius: 20rpx;
  398 +
  399 + .icon {
  400 + width: 28rpx;
  401 + height: 28rpx;
  402 + margin-right: 15rpx;
  403 + }
  404 + }
  405 +
  406 + .historyData-bottom {
  407 + margin-top: 30rpx;
  408 + background-color: #fff;
  409 + border-radius: 20rpx;
  410 +
  411 + .table {
  412 + border: 0px solid darkgray;
  413 +
  414 + .tr {
  415 + display: flex;
  416 + width: 100%;
  417 + justify-content: center;
  418 + height: 3rem;
  419 + align-items: center;
  420 +
  421 + .th {
  422 + display: flex;
  423 + justify-content: center;
  424 + align-items: center;
  425 + width: 50%;
  426 + color: #333;
  427 + font-weight: 500;
  428 + }
  429 +
  430 + .td {
  431 + color: #999;
  432 + width: 50%;
  433 + display: flex;
  434 + justify-content: center;
  435 + text-align: center;
  436 + }
  437 + }
  438 + }
  439 + }
  440 + }
  441 +
  442 + .odd {
  443 + background-color: #f9fcff;
  444 + }
445 445 </style>
... ...
  1 +<template>
  2 + <view class="mp-u-modal">
  3 + <u-modal :mask-close-able="true" :show="showModal" closeOnClickOverlay :showConfirmButton="false"
  4 + @close="$emit('hideModal')" @touchmove.stop.prevent="disabledScroll" z-index="99999">
  5 + <view class="w-100 modal-content">
  6 + <view class="header-title">命令下发</view>
  7 + <view class="u-flex">
  8 + <text class="type-text">下发类型:</text>
  9 + <u-radio-group v-model="commandType" placement="row">
  10 + <u-radio activeColor="#3388FF" label="单向" name="OneWay"></u-radio>
  11 + <view style="margin: 0 20rpx;"></view>
  12 + <u-radio activeColor="#3388FF" label="双向" name="TwoWay"></u-radio>
  13 + </u-radio-group>
  14 + </view>
  15 + <view class="content-body">
  16 + <div class="u-flex u-row-between">
  17 + <u--textarea :placeholder="`请输入下发内容${isShowTCP?'(字符串格式)':'(json格式)'}`"
  18 + v-model="inputCommandVal" />
  19 + <u-icon v-if="!isShowTCP" @click="handleCopy(copyTextValue)" name="question-circle"
  20 + color="#2979ff" size="28" class="ml-10">
  21 + </u-icon>
  22 +
  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>
\ No newline at end of file
... ...
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 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 +}
\ No newline at end of file
... ...
device-subpackage/device-detail/device-detail.vue renamed from deviceSubPage/deviceDetailPage/deviceDetail.vue
1   -<template>
2   - <view class="device-detail-page">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <u-sticky bgColor="#fff">
6   - <u-tabs :list="list" :current="currentTab" @click="handleTabClick" :activeStyle="{
7   - fontWeight: 'bold',
8   - color: '#333',
9   - }" :inactiveStyle="{
10   - color: '#999',
11   - }" :scrollable="isScrollable" />
12   - </u-sticky>
13   - <view style="margin-top: 30rpx">
14   - <basicInfo v-show="currentTab == 0" :deviceDetail="deviceDetail" />
15   - <realTimeData v-show="currentTab === 1" :recordList="recordList" />
16   - <historyData v-if="currentTab === 2" :keys="keys" :yesterday="yesterday" :today="today" :timeDiff="timeDiff"
17   - :historyData="historyData" :entityId="entityId" :start="startTs" :end="endTs" @update="handleUpdate" />
18   - <alarmHistory v-show="currentTab === 3" :deviceId="deviceId" />
19   - <commondRecord v-if="currentTab === 4" :tbDeviceId="entityId" />
20   - </view>
21   - </view>
22   -</template>
23   -
24   -<script>
25   - import fTabbar from "@/components/module/f-tabbar/f-tabbar";
26   - import basicInfo from "./tabDetail/basicInfo.vue";
27   - import realTimeData from "./tabDetail/realtimeData.vue";
28   - import alarmHistory from "./tabDetail/alarmHistory.vue";
29   - import historyData from "./tabDetail/historyData.vue";
30   - import commondRecord from "./tabDetail/CommandRecord.vue";
31   - import {
32   - getDeviceKeys,
33   - getHistoryData
34   - } from "./api/index.js";
35   - import {
36   - formatToDate
37   - } from "@/plugins/utils.js";
38   - import MescrollCompMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mixins/mescroll-comp.js";
39   - import moment from "moment";
40   - import base from "@/config/baseUrl.js";
41   -
42   - export default {
43   - mixins: [MescrollCompMixin],
44   - components: {
45   - fTabbar,
46   - basicInfo,
47   - realTimeData,
48   - alarmHistory,
49   - historyData,
50   - commondRecord,
51   - },
52   - data() {
53   - return {
54   - list: [{
55   - name: "基础信息",
56   - },
57   - {
58   - name: "实时数据",
59   - },
60   - {
61   - name: "历史数据",
62   - },
63   - {
64   - name: "告警记录",
65   - },
66   - ],
67   - currentTab: 0,
68   - deviceId: "",
69   - deviceDetail: {},
70   - keys: [],
71   - yesterday: "",
72   - today: "",
73   - timeDiff: "",
74   - historyData: [],
75   - entityId: "",
76   - startTs: "",
77   - endTs: "",
78   - recordList: [],
79   - isScrollable: false,
80   - attrList: []
81   - };
82   - },
83   - onUnload() {
84   - // 页面关闭时,销毁webSocket连接,否则第二次会存在连接不到的情况
85   - uni.closeSocket();
86   - },
87   - async onLoad(options) {
88   - const {
89   - id,
90   - alarmStatus,
91   - lastOnlineTime,
92   - tbDeviceId,
93   - deviceProfileId
94   - } =
95   - options;
96   - this.deviceId = id;
97   - const res = await uni.$u.http.get(`/yt/device/${id}`);
98   - this.deviceDetail = {
99   - ...res,
100   - alarmStatus,
101   - lastOnlineTime,
102   - };
103   -
104   - // 设备类型不是网关子设备的添加一个命令记录的选项卡
105   - if (this.deviceDetail.deviceType !== "SENSOR") {
106   - this.list.push({
107   - name: "命令记录",
108   - });
109   - }
110   - this.isScrollable = this.list.length > 4;
111   - if (res.deviceProfileId) {
112   - const getAttrList = await uni.$u.http.get(
113   - `/yt/device/attributes/${res.deviceProfileId}`
114   - );
115   - if (Array.isArray(getAttrList)) {
116   - this.attrList = getAttrList.map(m => {
117   - return m.identifier
118   - })
119   - }
120   - }
121   - // 连接webSockte
122   - const socketTask = uni.connectSocket({
123   - url: `${base.socketPrefix}://${base.baseWebSocketUrl}/api/ws/plugins/telemetry?token=` +
124   - uni.getStorageSync("userInfo").isToken, //仅为示例,并非真实接口地址。
125   - complete: () => {},
126   - });
127   - uni.onSocketOpen((header) => {
128   - socketTask.send({
129   - data: JSON.stringify({
130   - attrSubCmds: [],
131   - tsSubCmds: [{
132   - entityType: "DEVICE",
133   - entityId: tbDeviceId,
134   - scope: "LATEST_TELEMETRY",
135   - cmdId: 1,
136   - keys: this.attrList.join(','),
137   - }, ],
138   - historyCmds: [],
139   - entityDataCmds: [],
140   - entityDataUnsubscribeCmds: [],
141   - alarmDataCmds: [],
142   - alarmDataUnsubscribeCmds: [],
143   - entityCountCmds: [],
144   - entityCountUnsubscribeCmds: [],
145   - }),
146   - success() {},
147   - });
148   - });
149   - socketTask.onMessage((msg) => {
150   - const {
151   - data
152   - } = JSON.parse(msg.data);
153   - const newArray = [];
154   - for (const key in data) {
155   - const [time, value] = data[key].flat(1);
156   - let obj = {
157   - key,
158   - time,
159   - value,
160   - };
161   - if (this.recordList.length === 0) {
162   - this.recordList.unshift(obj);
163   - } else {
164   - newArray.push(obj);
165   - }
166   - }
167   - newArray.forEach((item) => {
168   - let flag = false;
169   - this.recordList.forEach((item1) => {
170   - if (item1.key === item.key) {
171   - item1.value = item.value;
172   - item1.time = item.time;
173   - flag = true;
174   - }
175   - });
176   - if (!flag) {
177   - this.recordList.unshift(item);
178   - }
179   - });
180   - this.recordList = this.recordList.map((item) => {
181   - return {
182   - ...item,
183   - time: formatToDate(item.time, "YYYY-MM-DD HH:mm:ss"),
184   - };
185   - });
186   - });
187   -
188   - const keys = await getDeviceKeys(tbDeviceId);
189   - this.keys = [keys];
190   - // 昨天
191   - this.yesterday = moment().subtract(1, "days").format("YYYY-MM-DD");
192   - // 今天
193   - this.today = moment().format("YYYY-MM-DD");
194   - // 开始时间
195   - this.startTs = moment().subtract(1, "days").format("x");
196   - // 结束时间
197   - this.endTs = moment().format("x");
198   - this.entityId = tbDeviceId;
199   -
200   - const data = await getHistoryData({
201   - entityId: tbDeviceId,
202   - startTs: this.startTs,
203   - endTs: this.endTs,
204   - keys: keys[0],
205   - // interval: 1800000,
206   - limit: 7,
207   - agg: 'NONE'
208   - });
209   - this.timeDiff = "30分钟";
210   - if (!Object.keys(data).length) return;
211   -
212   - this.historyData = data[keys[0]].map((item) => {
213   - return {
214   - value: item.value,
215   - ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
216   - };
217   - });
218   - },
219   - methods: {
220   - handleTabClick({
221   - index
222   - }) {
223   - this.currentTab = index;
224   - },
225   - handleUpdate(data, e) {
226   - if (!Array.isArray(data)) {
227   - this.historyData = [];
228   - return;
229   - }
230   - this.historyData = data.map((item) => {
231   - return {
232   - value: item.value,
233   - ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
234   - };
235   - });
236   - },
237   - },
238   - };
239   -</script>
240   -
241   -<style lang="scss" scoped>
242   - .device-detail-page {
243   - height: 100vh;
244   - background-color: #f8f9fa;
245   - }
246   -</style>
  1 +<template>
  2 + <view class="device-detail-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <u-sticky :bgColor="bgColor">
  6 + <u-tabs :list="list" :current="currentTab" @click="handleTabClick" :activeStyle="activeColor"
  7 + :inactiveStyle="inActiveColor" :scrollable="isScrollable" />
  8 + </u-sticky>
  9 + <view class="mt-3">
  10 + <basic-info v-show="currentTab == 0" :deviceDetail="deviceDetail" />
  11 + <realtime-data v-show="currentTab === 1" :recordList="recordList" />
  12 + <history-data v-if="currentTab === 2" :keys="keys" :yesterday="yesterday" :today="today"
  13 + :timeDiff="timeDiff" :historyData="historyData" :entityId="entityId" :start="startTs" :end="endTs"
  14 + @update="handleUpdate" />
  15 + <alarm-history v-show="currentTab === 3" :deviceId="deviceId" />
  16 + <commond-record v-if="currentTab === 4" :tbDeviceId="entityId" />
  17 + </view>
  18 + </view>
  19 +</template>
  20 +
  21 +<script>
  22 + import fTabbar from "@/components/module/f-tabbar/f-tabbar";
  23 + import basicInfo from "./components/basic-info.vue";
  24 + import realtimeData from "./components/realtime-data.vue";
  25 + import alarmHistory from "./components/alarm-history.vue";
  26 + import historyData from "./components/history-data.vue";
  27 + import commondRecord from "./components/command-record.vue";
  28 + import {
  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>
\ No newline at end of file
... ...
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 140 </style>
\ No newline at end of file
... ...
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 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>
\ No newline at end of file
... ...
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 49 "style": {
50 50 "navigationBarTitleText": "查看组态"
51 51 }
52   -
53 52 },
54 53 {
55   - "path": "pages/index/configuration/configurationDetail",
  54 + "path": "pages/index/configuration/configuration-detail",
56 55 "style": {
57 56 "navigationBarTitleText": "组态详情"
58 57 }
... ... @@ -65,30 +64,30 @@
65 64 }
66 65 ],
67 66 "subPackages": [{
68   - "root": "alarmSubPage",
  67 + "root": "alarm-subpackage",
69 68 "pages": [{
70   - "path": "alarmDetailPage/alarmDetail",
  69 + "path": "alarm-detail/alarm-detail",
71 70 "style": {
72 71 "navigationBarTitleText": "告警详情"
73 72 }
74 73 }]
75 74 },
76 75 {
77   - "root": "deviceSubPage",
  76 + "root": "device-subpackage",
78 77 "pages": [{
79   - "path": "deviceDetailPage/deviceDetail",
  78 + "path": "device-detail/device-detail",
80 79 "style": {
81 80 "navigationBarTitleText": "设备详情"
82 81 }
83 82 },
84 83 {
85   - "path": "deviceDetailPage/tabDetail/CommandDetail",
  84 + "path": "device-detail/components/command-detail",
86 85 "style": {
87 86 "navigationBarTitleText": "命令详情"
88 87 }
89 88 },
90 89 {
91   - "path": "deviceDetailPage/devicePosition",
  90 + "path": "device-detail/device-position",
92 91 "style": {
93 92 "navigationBarTitleText": "设备地理位置"
94 93 },
... ... @@ -104,15 +103,15 @@
104 103 ]
105 104 },
106 105 {
107   - "root": "sysNotifySubPage",
  106 + "root": "sysnotify-subpackage",
108 107 "pages": [{
109   - "path": "sysNotifyPage/systemNotify",
  108 + "path": "sys-notify/system-notify",
110 109 "style": {
111 110 "navigationBarTitleText": "系统通知"
112 111 }
113 112 },
114 113 {
115   - "path": "sysNotifyPage/notifyDetail",
  114 + "path": "sys-notify/notify-detail",
116 115 "style": {
117 116 "navigationBarTitleText": "通知详情"
118 117 }
... ... @@ -120,7 +119,7 @@
120 119 ]
121 120 },
122 121 {
123   - "root": "feedBackSubPage",
  122 + "root": "feedback-subpackage",
124 123 "pages": [{
125 124 "path": "feedback/feedback",
126 125 "style": {
... ... @@ -129,7 +128,7 @@
129 128 }]
130 129 },
131 130 {
132   - "root": "publicLoginSubPage",
  131 + "root": "login-subpackage",
133 132 "pages": [{
134 133 "path": "public/login",
135 134 "style": {
... ... @@ -149,7 +148,7 @@
149 148 }
150 149 },
151 150 {
152   - "path": "other/findPassword",
  151 + "path": "other/find-password",
153 152 "style": {
154 153 "navigationBarTitleText": "忘记密码"
155 154 }
... ... @@ -159,7 +158,7 @@
159 158 ],
160 159 "globalStyle": {
161 160 "navigationBarTextStyle": "black",
162   - "navigationBarTitleText": "云腾app",
  161 + "navigationBarTitleText": "ThingsKit",
163 162 "navigationBarBackgroundColor": "#FFFFFF",
164 163 "backgroundColor": "#FFFFFF",
165 164 "backgroundColorTop": "#FFFFFF"
... ...
... ... @@ -140,8 +140,8 @@
140 140 downCallback() {
141 141 this.list.length = 0;
142 142 this.page.num = 1;
143   - this.loadData(this.page.num);
144 143 this.resetQuery();
  144 + this.loadData(this.page.num);
145 145 },
146 146 //上拉加载
147 147 upCallback() {
... ... @@ -192,7 +192,7 @@
192 192 },
193 193 //跳转告警详情
194 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 11 @up="upCallback">
12 12 <device-item :list="list" @openDeviceDetail="openDeviceDetail"></device-item>
13 13 </mescroll-body>
  14 + <view style="height: 20rpx"></view>
14 15 <!-- 设备筛选 -->
15 16 <device-popup ref="devicePopupRef" :show="show" @close="close"
16 17 @queryCondition="getQueryCondition"></device-popup>
... ... @@ -173,7 +174,7 @@
173 174 },
174 175 openDeviceDetail(id, alarmStatus, lastOnlineTime, tbDeviceId) {
175 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 73 </style>
... ...
... ... @@ -71,7 +71,7 @@
71 71 },
72 72 openConfigDetail(e) {
73 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 17 export default {
18 18 data() {
19 19 return {
20   - staticLogo: '../../static/logo.png',
  20 + staticLogo: '/static/logo.png',
21 21 mpOwnConfig: {}
22 22 };
23 23 },
... ... @@ -30,7 +30,7 @@
30 30 created() {
31 31 if (!this.userInfo.isToken) {
32 32 setTimeout(() => {
33   - useReLaunch('/publicLoginSubPage/public/login')
  33 + useReLaunch('/login-subpackage/public/login')
34 34 }, 1000);
35 35 } else {
36 36 setTimeout(() => {
... ...
... ... @@ -97,13 +97,13 @@
97 97 showLogout: false,
98 98 thirdObj: {},
99 99 systemList: [{
100   - url: '/sysNotifySubPage/sysNotifyPage/systemNotify',
  100 + url: '/sysnotify-subpackage/sys-notify/system-notify',
101 101 text: '系统通知',
102 102 leftIcon: '/static/sys-not.png',
103 103 rightIcon: '/static/arrow-right.png'
104 104 },
105 105 {
106   - url: '/feedBackSubPage/feedback/feedback',
  106 + url: '/feedback-subpackage/feedback/feedback',
107 107 text: '意见反馈',
108 108 leftIcon: '/static/find-sugg.png',
109 109 rightIcon: '/static/arrow-right.png'
... ... @@ -139,14 +139,14 @@
139 139 useNavigateTo(url)
140 140 },
141 141 navigatorLogin() {
142   - useNavigateTo('/publicLoginSubPage/public/login')
  142 + useNavigateTo('/login-subpackage/public/login')
143 143 },
144 144 navigatorPersonal() {
145 145 let data = {
146 146 data: this.userInfo,
147 147 third: this.thirdObj
148 148 };
149   - useNavigateTo('/publicLoginSubPage/other/set?data=', data)
  149 + useNavigateTo('/login-subpackage/other/set?data=', data)
150 150 },
151 151 openBindAccountModal() {
152 152 this.showBindAccount = true;
... ... @@ -164,7 +164,7 @@
164 164 if (res.confirm) {
165 165 that.emptyUserInfo();
166 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 47 white-space: nowrap;
48 48 }
49 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 79 .ml-10{
52 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>
... ...