Commit 62cf4308dc566da5a6fd7cbafc564eb1109827a4

Authored by xp.Huang
2 parents 3e274017 15cac584

Merge branch 'local_dev_ft' into 'main_dev'

perf: 优化重构app完成,优化主分包体积大小,主包不超过1.5M

See merge request yunteng/thingskit-app!121
Showing 101 changed files with 6367 additions and 6311 deletions
1 1 <script>
2 2 import store from '@/store';
3   - // import {
4   - // scene
5   - // } from '@/config/common';
6 3
7 4 export default {
8 5 //设置全局变量,解绑时从这里取
... ... @@ -10,27 +7,12 @@
10 7 openId: ''
11 8 },
12 9 onLaunch(e) {
13   - //取出缓存数据
14   - // store.commit('setCacheData');
15   - //获取二维码信息
16   - // scene(e);
17 10 // #ifdef APP-PLUS
18 11 uni.reLaunch({
19 12 url: '/pages/index/splash'
20 13 })
21 14 // #endif
22 15 },
23   - onShow(e) {
24   - // #ifdef MP
25   - // mpUpData(); //检测小程序更新
26   - // #endif
27   - // #ifdef APP-PLUS
28   - // uni.reLaunch({
29   - // url: '/pages/index/splash'
30   - // })
31   - // #endif
32   - },
33   - onHide() {},
34 16 onUnload(){
35 17 uni.setStorageSync('getConfiguration', {
36 18 isConfiguration: false
... ...
  1 +<template>
  2 + <view class="alarm-detail-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <view class="alarm-detail-column">
  6 + <view class="u-flex detail-column">
  7 + <view class="column-line" v-for="(item,index) in alarmDetail" :key="index">
  8 + <view class="column">
  9 + <text class="text-org-bold">{{item.label}}</text>
  10 + <text class="text-device-muted text-clip alarm-text"
  11 + :style="{color:hasColor.includes(item.label)?'#DE4437':''}">
  12 + {{item.label===hasColor[0]? setAlarmStatus(item.value):item.label===hasColor[1]?setAlarmSeverity(item.value):
  13 + item.label==='告警值:'?formatAlarmValueText:item.label==='告警条件:'?formatAlarmConditionText
  14 + :item.label==='告警设备:'?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 {deviceName,severity,organizationName,details,type,createdTime,status,id} = params
  97 + this.detailId = id
  98 + this.alarmDetail = [{label: '告警场景:',value: type},{label: '告警级别:',value: severity},{label: '所属组织:',value: organizationName},{label: '告警设备:',value: ''},{label: '告警条件:',value: ''},{label: '告警值:',value: ''},{label: '告警时间:',value: createdTime},{label: '告警状态:',value: status},]
  99 + this.formatAlarmDevice(details)
  100 + this.formatAlarmValue(details)
  101 + this.formatAlarmCondition(details)
  102 + }
  103 + // 隐藏原生的tabbar
  104 + uni.hideTabBar();
  105 + },
  106 + methods: {
  107 + ...mapActions(['updateBadgeTotal']),
  108 + setAlarmStatus(value) {
  109 + return this.alarmSeverity.find(item => item.value === value).label
  110 + },
  111 + setAlarmSeverity(value) {
  112 + return this.alarmStatus.find(item => item.value === value).label
  113 + },
  114 + returnPrevPage(title) {
  115 + useShowToast(title)
  116 + let pages = getCurrentPages(); //获取所有页面栈实例列表
  117 + let nowPage = pages[pages.length - 1]; //当前页页面实例
  118 + let prevPage = pages[pages.length - 2]; //上一页页面实例
  119 + prevPage.$vm.detailStatus = true;
  120 + },
  121 + async handleSubmit() {
  122 + if (this.formModel.result == '') return uni.$u.toast('请输入处理结果');
  123 + const res = await api.alarmApi.postAlarmAckApi(this.detailId)
  124 + if (res == '') {
  125 + this.returnPrevPage('处理成功~')
  126 + setTimeout(() => {
  127 + useNavigateBack(1)
  128 + }, 500);
  129 + }
  130 + },
  131 + async handleRemove() {
  132 + const res = await api.alarmApi.postAlarmClearApi(this.detailId)
  133 + if (res == '') {
  134 + this.returnPrevPage('清除成功~')
  135 + setTimeout(async () => {
  136 + useNavigateBack(1)
  137 + const res = await uni.$u.http.get('/yt/homepage/app?login=true');
  138 + if (res) {
  139 + //异步实时更新告警徽标数
  140 + await this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
  141 + }
  142 + }, 500);
  143 + }
  144 + },
  145 + //告警值处理
  146 + async formatAlarmValue(e) {
  147 + const keys = Object.keys(e)
  148 + const dataFormat = await this.handleAlarmDetailFormat(keys);
  149 + const values = keys.reduce((acc, curr) => {
  150 + dataFormat.forEach((dataItem => {
  151 + if (dataItem.tbDeviceId === curr) {
  152 + const findAttribute = dataItem.attribute.find(findItem => findItem.identifier === e[curr].key)
  153 + acc.push(
  154 + `${findAttribute.name}:${e[curr].realValue}${!findAttribute.detail?.dataType?.specs?.unit?.key?'':findAttribute.detail?.dataType?.specs?.unit?.key}`
  155 + )
  156 + }
  157 + }))
  158 + return acc
  159 + }, [])
  160 + this.formatAlarmValueText = values.join(',')
  161 + },
  162 + //告警条件处理
  163 + formatAlarmCondition(e) {
  164 + const keys = Object.keys(e)
  165 + const values = keys.reduce((acc, curr) => {
  166 + acc.push({
  167 + login: e[curr].logic,
  168 + logicValue: e[curr].logicValue
  169 + })
  170 + return acc
  171 + }, [])
  172 + const operation = [...operationNumberOrDate, ...operationString, ...operationBoolean]
  173 + const format = values.map(item => {
  174 + const findOperation = operation.find(findItem => findItem.value === item.login)?.symbol
  175 + return findOperation + item.logicValue
  176 + })
  177 + this.formatAlarmConditionText = format.filter(Boolean).join(',')
  178 + },
  179 + //告警设备处理
  180 + async formatAlarmDevice(e) {
  181 + const keys = Object.keys(e)
  182 + const dataFormat = await this.handleAlarmDetailFormat(keys);
  183 + if (!dataFormat) this.formatDeviceText = ''
  184 + if (Array.isArray(dataFormat) && dataFormat.length === 0) this.formatDeviceText = ''
  185 + this.formatDeviceText = dataFormat.map(item => item.name).join(',')
  186 + },
  187 + async handleAlarmDetailFormat(keys) {
  188 + const temp = [];
  189 + for (let item of keys) {
  190 + if (item === 'key' || item === 'data') return; //旧数据则终止
  191 + const deviceDetailRes = await api.deviceApi.getDeviceDetail(item);
  192 + const { deviceProfileId } = deviceDetailRes;
  193 + if (!deviceProfileId) return;
  194 + const attributeRes = await api.deviceApi.getAttribute(deviceProfileId);
  195 + const dataFormat = this.handleDataFormat(deviceDetailRes, attributeRes);
  196 + temp.push(dataFormat);
  197 + }
  198 + return temp;
  199 + },
  200 + handleDataFormat(deviceDetail, attributes) {
  201 + const { name,tbDeviceId } = deviceDetail;
  202 + const attribute = attributes.map((item) => ({
  203 + identifier: item.identifier,
  204 + name: item.name,
  205 + detail: item.detail
  206 + }));
  207 + return {
  208 + name,
  209 + tbDeviceId,
  210 + attribute,
  211 + };
  212 + }
  213 + }
  214 + };
  215 +</script>
  216 +
  217 +<style lang="scss" scoped>
  218 + @import './static/alarmDetail.scss';
  219 +
  220 + /deep/ .u-button--primary {
  221 + background-color: #377dff !important;
  222 + border-color: #377dff !important;
  223 + }
  224 +</style>
\ No newline at end of file
... ...
alarm-subpackage/alarm-detail/static/alarmDetail.scss renamed from alarmSubPage/alarmDetailPage/static/alarmDetail.scss
... ... @@ -6,57 +6,67 @@
6 6 .alarm-detail-column {
7 7 border-radius: 10px;
8 8 width: 688rpx;
9   - height: 573rpx;
  9 + height: 688rpx;
10 10 background-color: #ffffff;
11 11 .detail-column {
12   - height: 573rpx;
13   - justify-content: space-between;
  12 + width:750rpx;
14 13 flex-direction: column;
15 14 align-items: center;
16   - .column {
17   - flex-direction: row;
  15 + .column-line{
18 16 display:flex;
19   - // justify-content: space-between;
20   - margin-top: 10rpx;
21   - line-height: 68rpx;
22   - width: 614rpx;
23   - height: 90rpx;
24   - text-align: left;
25   - border-bottom: 0.1rpx solid #f0f0f0;
26   - .device-name{
27   - width:222rpx;
28   - }
29   - .text {
30   - color: #333333;
31   - font-size: 15px;
32   - }
33   - .image {
34   - width: 30rpx;
35   - height: 30rpx;
36   - }
37   - .text-alarm-level {
38   - color: #333333;
39   - font-size: 14px;
40   - }
41   - .text-alarm-level-lg {
42   - color: #333333;
43   - font-size: 15px;
44   - }
45   - .text-alarm-lg {
46   - color: #666666;
47   - font-size: 14px;
48   - }
49   - .text-alarm-status {
50   - color: #de4437;
51   - font-size: 14px;
  17 + flex-direction: column;
  18 + .column {
  19 + display:flex;
  20 + margin-top: 10rpx;
  21 + line-height: 68rpx;
  22 + width:700rpx;
  23 + height: 74rpx;
  24 + align-items: center;
  25 + .device-name{
  26 + width:222rpx;
  27 + }
  28 + .alarm-text{
  29 + width:460rpx;
  30 + overflow-x: scroll;
  31 + }
  32 +
  33 + .text {
  34 + color: #333333;
  35 + font-size: 15px;
  36 + }
  37 + .image {
  38 + width: 30rpx;
  39 + height: 30rpx;
  40 + }
  41 + .text-alarm-level {
  42 + color: #333333;
  43 + font-size: 14px;
  44 + }
  45 + .text-alarm-level-lg {
  46 + color: #333333;
  47 + font-size: 15px;
  48 + }
  49 + .text-alarm-lg {
  50 + color: #666666;
  51 + font-size: 14px;
  52 + }
  53 + .text-alarm-status {
  54 + color: #de4437;
  55 + font-size: 14px;
  56 + }
52 57 }
  58 + .bottom-line{
  59 + border-bottom: 0.1rpx solid #f0f0f0;
  60 + width:650rpx;
  61 + }
53 62 }
  63 +
54 64 }
55 65 }
56 66 .handle-result {
57 67 color: #333333;
58 68 font-size: 15px;
59   - margin-top: 20rpx;
  69 + margin-top: 32rpx;
60 70 }
61 71 .hanle-main {
62 72 margin-top: 30rpx;
... ... @@ -65,3 +75,13 @@
65 75 height: 273rpx;
66 76 background-color: #ffffff;
67 77 }
  78 +.bottom-button{
  79 + margin-top: 44rpx;
  80 + display: flex;
  81 + align-items: center;
  82 + gap:40rpx;
  83 + justify-content: center;
  84 + .button-item{
  85 + width: 260rpx;
  86 + }
  87 +}
\ No newline at end of file
... ...
1   -<template>
2   - <view class="alarm-detail-page">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <view class="alarm-detail-column">
6   - <view class="u-flex detail-column">
7   - <view class="u-flex column">
8   - <text
9   - class="text-clip device-name text-org-bold">{{ list.deviceName == null ? '暂无数据' : list.deviceName }}</text>
10   - <image class="image" src="/static/alarm-device.png"></image>
11   - </view>
12   - <view class="column">
13   - <text class="text-org-bold ">告警级别:</text>
14   - <text class="text-device-muted" style="color:#DE4437">
15   - {{
16   - list.severity == 'CRITICAL'
17   - ? '危险'
18   - : list.severity == 'MAJOR'
19   - ? '重要'
20   - : list.severity == 'MINOR'
21   - ? '次要'
22   - : list.severity == 'WARNING'
23   - ? '警告'
24   - : '不确定'
25   - }}
26   - </text>
27   - </view>
28   - <view class="column">
29   - <text class="text-org-bold">所属组织:</text>
30   - <text
31   - class="text-device-muted">{{ list.organizationName == null ? '暂无数据' : list.organizationName }}</text>
32   - </view>
33   - <view class="column">
34   - <text class="text-org-bold">告警值:</text>
35   - <text
36   - class="text-device-muted text-clip">{{ list.details == null ? '暂无数据' : formatDetailText(list.details.data) }}</text>
37   - </view>
38   - <view class="column">
39   - <text class="text-org-bold">告警场景:</text>
40   - <text
41   - class="text-device-muted text-clip">{{ list.type == null ? '暂无数据' : list.type }}</text>
42   - </view>
43   - <view class="column">
44   - <text class="text-org-bold">告警时间:</text>
45   - <text class="text-device-muted">{{ list.createdTime }}</text>
46   - </view>
47   - <view class="column">
48   - <text class="text-org-bold">告警状态:</text>
49   - <text class="text-device-muted" style="color: #DE4437;">
50   - {{
51   - list.status == 'CLEARED_UNACK'
52   - ? '清除未确认'
53   - : list.status == 'ACTIVE_UNACK'
54   - ? '激活未确认'
55   - : list.status == 'CLEARED_ACK'
56   - ? '清除已确认'
57   - : '激活已确认'
58   - }}
59   - </text>
60   - </view>
61   - </view>
62   - </view>
63   - <!-- #ifdef MP -->
64   - <view class="handle-result text-org-bold" style="">处理结果</view>
65   - <view class="hanle-main">
66   - <u--form :label-style="{ 'font-size': '0rpx' }" style="padding-left: 26rpx;" labelPosition="left"
67   - :model="formModel" ref="form1">
68   - <u-form-item label="." prop="result" ref="item3">
69   - <view style="position: relative;left: -60rpx;">
70   - <u--textarea border="none" height="96" placeholder="请输入处理结果" v-model="formModel.result" count>
71   - </u--textarea>
72   - </view>
73   - </u-form-item>
74   - </u--form>
75   - </view>
76   - <!-- #endif -->
77   - <!-- #ifdef APP-PLUS -->
78   - <view class="handle-result text-org-bold">处理结果</view>
79   - <view class="hanle-main">
80   - <view>
81   - <u--textarea border="none" height="96" placeholder="请输入处理结果" v-model="formModel.result" count>
82   - </u--textarea>
83   - </view>
84   - </view>
85   - <!-- #endif -->
86   - <view style="margin-top: 44rpx;display: flex;align-items: center;justify-content: space-between;">
87   - <view :style="[
88   - { position: list.status !== 'CLEARED_ACK' && list.status !== 'ACTIVE_ACK' ? 'relative' : '' },
89   - { right: list.status !== 'CLEARED_ACK' && list.status !== 'ACTIVE_ACK' ? '-210rpx' : '' }
90   - ]" v-if="list.status !== 'CLEARED_ACK' && list.status !== 'ACTIVE_ACK'" class="u-flex" style="width: 260rpx">
91   - <u-button @click="handleSubmit" type="primary" shape="circle" text="处理"></u-button>
92   - </view>
93   - <view style="width: 30rpx;"></view>
94   - <view
95   - :style="[{ position: list.status == 'ACTIVE_ACK' ? 'relative' : '' }, { right: list.status == 'ACTIVE_ACK' ? '207rpx' : '' }]"
96   - v-if="list.status == 'ACTIVE_ACK'" class="u-flex" style="width: 260rpx">
97   - <u-button @click="handleRemove" type="error" shape="circle" text="清除"></u-button>
98   - </view>
99   - </view>
100   - </view>
101   -</template>
102   -
103   -<script>
104   - import {
105   - mapActions
106   - } from 'vuex'
107   - import api from '@/api/index.js'
108   -
109   - export default {
110   - data() {
111   - return {
112   - formModel: {
113   - result: ''
114   - },
115   - list: {}
116   - };
117   - },
118   - onLoad(e) {
119   - if (e.data !== null) {
120   - let params = JSON.parse(e.data);
121   - this.list = params;
122   - }
123   - // 隐藏原生的tabbar
124   - uni.hideTabBar();
125   - },
126   - methods: {
127   - //处理
128   - async handleSubmit() {
129   - if (this.formModel.result == '') return uni.$u.toast('请输入处理结果');
130   - const res = await api.alarmApi.postAlarmAckApi(this.list.id)
131   - if (res == '') {
132   - uni.showToast({
133   - title: '处理成功~',
134   - icon: 'none'
135   - });
136   - let pages = getCurrentPages(); //获取所有页面栈实例列表
137   - let nowPage = pages[pages.length - 1]; //当前页页面实例
138   - let prevPage = pages[pages.length - 2]; //上一页页面实例
139   - prevPage.$vm.detailStatus = true;
140   - setTimeout(() => {
141   - uni.navigateBack({
142   - delta: 1
143   - });
144   - }, 500);
145   - }
146   - },
147   - // 清除
148   - async handleRemove() {
149   - const res = await api.alarmApi.postAlarmClearApi(this.list.id)
150   - if (res == '') {
151   - uni.showToast({
152   - title: '清除成功~',
153   - icon: 'none'
154   - });
155   - let pages = getCurrentPages(); //获取所有页面栈实例列表
156   - let nowPage = pages[pages.length - 1]; //当前页页面实例
157   - let prevPage = pages[pages.length - 2]; //上一页页面实例
158   - prevPage.$vm.detailStatus = true;
159   - setTimeout(async () => {
160   - uni.navigateBack({
161   - delta: 1
162   - });
163   - const res = await uni.$u.http.get('/yt/homepage/app?login=true');
164   - if (res) {
165   - //异步实时更新告警徽标数
166   - await this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
167   - }
168   - }, 500);
169   - }
170   - },
171   - ...mapActions(['updateBadgeTotal']),
172   - formatDetailText(e) {
173   - //去除字符串双引号
174   - const jsonStr = JSON.stringify(e);
175   - const str = jsonStr.substring(1, jsonStr.length - 1);
176   - const newStr = str.replace(/\"/g, '');
177   - return newStr.slice(0,26);
178   - }
179   - }
180   - };
181   -</script>
182   -
183   -<style lang="scss" scoped>
184   - @import './static/alarmDetail.scss';
185   -
186   - /deep/ .u-button--primary {
187   - background-color: #377dff !important;
188   - border-color: #377dff !important;
189   - }
190   -</style>
... ... @@ -11,6 +11,30 @@ const getDeviceApi = (urlParams, data) => {
11 11 return uni.$u.http.post(`/yt/device?page=${page}&pageSize=${pageSize}`, data);
12 12 };
13 13
14   -export default {
15   - getDeviceApi
  14 +// 设备详情
  15 +const getDeviceDetail = (id) => {
  16 + return uni.$u.http.get(`/yt/device/${id}`);
  17 +};
  18 +
  19 +//设备属性
  20 +const getAttribute = (deviceProfileId) => {
  21 + return uni.$u.http.get(`/yt/device/attributes/${deviceProfileId}`);
  22 +};
  23 +
  24 +//命令下发
  25 +const issueCommand = (type, tbDeviceId, data) => {
  26 + return uni.$u.http.post(`/rpc/${type==='OneWay'?'oneway':'twoway'}/${tbDeviceId}`, data)
16 27 }
  28 +
  29 +//获取命令下发记录
  30 +const getRpcRecord = (params) => {
  31 + return uni.$u.http.get('/yt/rpc', params);
  32 +};
  33 +
  34 +export default {
  35 + getDeviceApi,
  36 + getDeviceDetail,
  37 + getAttribute,
  38 + issueCommand,
  39 + getRpcRecord
  40 +}
\ No newline at end of file
... ...
... ... @@ -20,9 +20,16 @@ const getConfigurationApi = (params = {}) => {
20 20 .get('/yt/configuration/center', params)
21 21 }
22 22
  23 +//获取组织列表
  24 +const getMeOrgListApi = () => {
  25 + return uni.$u.http
  26 + .get('/yt/organization/me/list')
  27 +}
  28 +
23 29 export default {
24 30 getHomeStatisticsApi,
25 31 getCameraApi,
26 32 byCameraIdGetDetailApi,
27   - getConfigurationApi
  33 + getConfigurationApi,
  34 + getMeOrgListApi
28 35 }
... ...
... ... @@ -3,12 +3,18 @@ const getPlateCustomApi = () => {
3 3 return uni.$u.http.get("/yt/app_design/get")
4 4 };
5 5
6   -//第三方账号绑定API
  6 +//第三方账号登录API
7 7 const getThirdLoginApi = (code) => {
8 8 return uni.$u.http.get(`/yt/third/login/${code}`)
9 9 };
10 10
11   -//获取个人信息API
  11 +//第三方账号绑定API
  12 +const postThirdLoginApi = (data = {}) => {
  13 + return uni.$u.http
  14 + .post("/yt/third/bind", data)
  15 +};
  16 +
  17 +//设置个人信息API
12 18 const setUserInfoApi = () => {
13 19 return uni.$u.http.get("/yt/user/me/info")
14 20 };
... ... @@ -52,6 +58,18 @@ const deleteBindApi = (data = {}) => {
52 58 return uni.$u.http.delete('/yt/third', data)
53 59 }
54 60
  61 +//获取手机验证码API
  62 +const postPhoneCodeApi = (phone, data = {}) => {
  63 + return uni.$u.http
  64 + .post(`/yt/noauth/send_login_code/${phone}`, data)
  65 +}
  66 +
  67 +//手机登录API
  68 +const postPhoneLoginApi = (data = {}) => {
  69 + return uni.$u.http
  70 + .post('/yt/auth/code/login', data)
  71 +}
  72 +
55 73 export default {
56 74 getPlateCustomApi,
57 75 getThirdLoginApi,
... ... @@ -62,5 +80,8 @@ export default {
62 80 postCodeApi,
63 81 postResetCodeApi,
64 82 postPersonalInfoApi,
65   - deleteBindApi
  83 + deleteBindApi,
  84 + postPhoneCodeApi,
  85 + postPhoneLoginApi,
  86 + postThirdLoginApi
66 87 }
... ...
  1 +<template>
  2 + <view class="header-org">
  3 + <view @click="$emit('openOrg')" class="org-item">
  4 + <view class="u-flex org-contact"><text class="text">组织关系</text></view>
  5 + <view class="u-flex org-device">
  6 + <image class="device-image" :src="imageSrc"></image>
  7 + <text class="device-text">摄像头数:{{ total }}</text>
  8 + </view>
  9 + </view>
  10 + <view class="org-item">
  11 + <image class="image" src="/static/arrow-right.png"></image>
  12 + </view>
  13 + </view>
  14 +</template>
  15 +
  16 +<script>
  17 + export default {
  18 + props: {
  19 + total: Number,
  20 + imageSrc: String
  21 + }
  22 + }
  23 +</script>
  24 +
  25 +<style lang="scss" scoped>
  26 + .header-org {
  27 + width: 750rpx;
  28 + height: 150rpx;
  29 + margin-top: 1rpx;
  30 + background-color: #fff;
  31 + display: flex;
  32 + flex-direction: row;
  33 + justify-content: space-between;
  34 + position: fixed;
  35 + z-index: 999999;
  36 + top: -1rpx;
  37 +
  38 + .org-item {
  39 + width: 350rpx;
  40 + height: 200rpx;
  41 +
  42 + .org-contact {
  43 + flex-direction: row;
  44 + margin-top: 26rpx;
  45 + margin-left: 15rpx;
  46 +
  47 + .text {
  48 + color: #333333;
  49 + font-size: 15px;
  50 + margin-left: 14rpx;
  51 + }
  52 + }
  53 +
  54 + .org-device {
  55 + margin-top: 23rpx;
  56 + margin-left: 15rpx;
  57 + flex-direction: row;
  58 +
  59 + .device-image {
  60 + margin-left: 14rpx;
  61 + width: 30rpx;
  62 + height: 30rpx;
  63 + }
  64 +
  65 + .device-text {
  66 + margin-left: 10rpx;
  67 + color: #666666;
  68 + font-size: 12px;
  69 + }
  70 + }
  71 +
  72 + .image {
  73 + width: 6px;
  74 + height: 10px;
  75 + float: right;
  76 + margin-right: 34rpx;
  77 + margin-top: 37rpx;
  78 + }
  79 + }
  80 + }
  81 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <u-sticky>
  3 + <view class="device-top">
  4 + <view class="search">
  5 + <view>
  6 + <view class="search-left">
  7 + <slot></slot>
  8 + </view>
  9 + </view>
  10 + <view @click="$emit('openSearchDialog')" class="search-right">
  11 + <text>筛选</text>
  12 + <image src="../../static/shaixuan.png" />
  13 + </view>
  14 + </view>
  15 + <u-line />
  16 + <view class="org">
  17 + <u-cell @click="$emit('openOrg')" isLink title="组织关系" :border="false">
  18 + <view slot="label" class="label" style="display: flex; align-items: center;margin-top: 20rpx;">
  19 + <image src="../../static/org.png" style="width: 24rpx;height: 28rpx;"></image>
  20 + <view style="margin-left: 10rpx; color: #666;">
  21 + {{totalText}}
  22 + <text style="margin-left: 20rpx;">{{ total }}</text>
  23 + </view>
  24 + </view>
  25 + </u-cell>
  26 + </view>
  27 + </view>
  28 + </u-sticky>
  29 +</template>
  30 +
  31 +<script>
  32 + export default {
  33 + props: {
  34 + total: Number,
  35 + totalText: [Number, String]
  36 + }
  37 + }
  38 +</script>
  39 +
  40 +<style lang="scss" scoped>
  41 + /deep/ .u-button--primary {
  42 + background-color: #377dff !important;
  43 + border-color: #377dff !important;
  44 + }
  45 +
  46 + /deep/ .u-button--info {
  47 + background-color: #e3e3e5 !important;
  48 + border-color: #e3e3e5 !important;
  49 + }
  50 +
  51 + /deep/ .u-cell__right-icon-wrap {
  52 + margin-top: -55rpx !important;
  53 + }
  54 +
  55 + /deep/ .uni-calendar--fixed {
  56 + bottom: 172rpx !important;
  57 + }
  58 +
  59 + .pop-no-scroll {
  60 + overflow: hidden;
  61 + position: fixed;
  62 + height: 100%;
  63 + width: 100%;
  64 + }
  65 +
  66 + .device-top {
  67 + padding: 10rpx 30rpx;
  68 + background-color: #fff;
  69 +
  70 + .search {
  71 + display: flex;
  72 + justify-content: space-between;
  73 + padding-bottom: 10rpx;
  74 +
  75 + .search-left {
  76 + width: 580rpx;
  77 + background-color: #f8f9fa;
  78 + border-radius: 200rpx;
  79 + }
  80 +
  81 + .search-right {
  82 + display: flex;
  83 + align-items: center;
  84 +
  85 + text {
  86 + color: #333;
  87 + font-size: 14px;
  88 + }
  89 +
  90 + image {
  91 + width: 40rpx;
  92 + height: 40rpx;
  93 + }
  94 + }
  95 + }
  96 + }
  97 +</style>
\ No newline at end of file
... ...
... ... @@ -228,7 +228,6 @@ export default {
228 228 if (pageLen == 1 && !mainPagePath.includes(currentPages[0].route)) {
229 229 this.firstPage = true;
230 230 this.iconLeft = 'home';
231   - console.log(this.firstPage, 'this.firstPage');
232 231 }
233 232 },
234 233 methods: {
... ... @@ -273,7 +272,6 @@ export default {
273 272 //设置手机状态栏颜色
274 273 settingColor() {
275 274 let navColor = this.navFontColor;
276   - console.log(navColor, 'settingColor');
277 275 let frontColor = '#000000';
278 276 if (whiteList.includes(navColor)) {
279 277 frontColor = '#ffffff';
... ...
... ... @@ -9,10 +9,10 @@ import {
9 9 * socketPrefix websocket前缀 ((https, wss),( http, ws))
10 10 */
11 11
12   -const baseUrl = "https://demo.thingskit.com/api";
13   -const baseDrawioUrl = "https://demo.thingskit.com/thingskit-scada";
14   -const baseWebSocketUrl = "demo.thingskit.com";
15   -const socketPrefix = "wss";
  12 +const baseUrl = "http://222.180.200.114:48080/api";
  13 +const baseDrawioUrl = "http://222.180.200.114:9527/thingskit-scada";
  14 +const baseWebSocketUrl = "222.180.200.114:48080";
  15 +const socketPrefix = "ws";
16 16
17 17 let systemInfo = {
18 18 ...getTabbarHeight(),
... ... @@ -51,4 +51,4 @@ const courtConfig = {
51 51 sk: "",
52 52 },
53 53 };
54   -export default Object.assign({}, courtConfig);
  54 +export default Object.assign({}, courtConfig);
... ...
... ... @@ -103,11 +103,11 @@ uni.$u.http.interceptors.response.use(
103 103 const routers = getCurrentPages();
104 104 const currentRoute = routers[routers.length - 1].route;
105 105 const isLoginPage = currentRoute.includes(
106   - "publicLoginSubPage/public/login"
  106 + "login-subpackage/public/login"
107 107 );
108 108 !isLoginPage &&
109 109 uni.reLaunch({
110   - url: "/publicLoginSubPage/public/login",
  110 + url: "/login-subpackage/public/login",
111 111 });
112 112 // 清空登录信息
113 113 store.commit("emptyUserInfo");
... ...
  1 +//上传图片大小
  2 +const UPLOAD_FILE_SIZE = 5242880
  3 +
  4 +export {
  5 + UPLOAD_FILE_SIZE
  6 +}
\ No newline at end of file
... ...
device-subpackage/device-detail/api/index.js renamed from deviceSubPage/deviceDetailPage/api/index.js
1   -// 获取某个Key的历史数据
2   -const getUrlParams = (params) => {
3   - return Object.keys(params).reduce((prev, next, currentIndex) => {
4   - if (params[next]) {
5   - return prev += `${!currentIndex ? '?' : '&'}${next}=${params[next]}`
6   - }
7   - return prev
8   - }, '')
9   -}
10   -
11   -export function getHistoryData(params) {
12   - let {
13   - entityId
14   - } = params
15   - params = getUrlParams(params)
16   - return uni.$u.http.get(
17   - `/plugins/telemetry/DEVICE/${entityId}/values/timeseries${params}`
18   - );
19   -}
20   -
21   -
22   -
23   -// 获取当前设备的key
24   -export function getDeviceKeys(id) {
25   - return uni.$u.http.get(`/plugins/telemetry/DEVICE/${id}/keys/timeseries`);
26   -};
27   -
28   -export function issueCommand(type, tbDeviceId, data) {
29   - return uni.$u.http.post(`/rpc/${type==='OneWay'?'oneway':'twoway'}/${tbDeviceId}`, data)
  1 +// 获取某个Key的历史数据
  2 +const getUrlParams = (params) => {
  3 + return Object.keys(params).reduce((prev, next, currentIndex) => {
  4 + if (params[next]) {
  5 + return prev += `${!currentIndex ? '?' : '&'}${next}=${params[next]}`
  6 + }
  7 + return prev
  8 + }, '')
30 9 }
  10 +
  11 +export function getHistoryData(params) {
  12 + let {
  13 + entityId
  14 + } = params
  15 + params = getUrlParams(params)
  16 + return uni.$u.http.get(
  17 + `/plugins/telemetry/DEVICE/${entityId}/values/timeseries${params}`
  18 + );
  19 +}
  20 +
  21 +
  22 +
  23 +// 获取当前设备的key
  24 +export function getDeviceKeys(id) {
  25 + return uni.$u.http.get(`/plugins/telemetry/DEVICE/${id}/keys/timeseries`);
  26 +};
... ...
  1 +<template>
  2 + <view class="alert-page">
  3 + <!-- 告警头部 -->
  4 + <view class="filter-button" @click="openSearchDialog">
  5 + <text>筛选</text>
  6 + <image src="/static/shaixuan.png" />
  7 + </view>
  8 + <!-- 告警分页 -->
  9 + <mescroll-uni height="700rpx;" ref="mescrollRef" @init="mescrollInit" :up="upOption" :down="downOption"
  10 + @down="downCallback" @up="upCallback">
  11 + <alarm-item :list="list" @openAlertDetail="openAlertDetail"></alarm-item>
  12 + <mescroll-empty v-if="!list.length" />
  13 + </mescroll-uni>
  14 + <view style="height: 20rpx"></view>
  15 + <!-- 告警筛选 -->
  16 + <alarm-popup ref="alarmPopupRef" :show="show" @close="close" @queryCondition="getQueryCondition"></alarm-popup>
  17 + </view>
  18 +</template>
  19 +<script>
  20 + import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
  21 + import api from '@/api/index.js'
  22 + import alarmItem from '@/pages/alarm/components/alarm-item.vue'
  23 + import alarmPopup from '@/pages/alarm/components/alarm-popup.vue'
  24 + import {
  25 + useNavigateTo
  26 + } from '@/plugins/utils.js'
  27 +
  28 + export default {
  29 + mixins: [MescrollMixin],
  30 + components: {
  31 + alarmItem,
  32 + alarmPopup
  33 + },
  34 + props: {
  35 + deviceId: {
  36 + type: String,
  37 + default: ''
  38 + }
  39 + },
  40 + data() {
  41 + return {
  42 + show: false,
  43 + list: [],
  44 + total: 0,
  45 + downOption: {
  46 + auto: true //是否在初始化后,自动执行downCallback; 默认true
  47 + },
  48 + upOption: {
  49 + auto: false // 不自动加载
  50 + },
  51 + page: {
  52 + num: 0,
  53 + size: 10
  54 + },
  55 + conditions: {},
  56 + };
  57 + },
  58 + methods: {
  59 + disabledScroll() {
  60 + return;
  61 + },
  62 + getQueryCondition(value) {
  63 + this.conditions = value
  64 + this.conditions.deviceId = this.deviceId
  65 + this.loadData(1, this.conditions);
  66 + this.close()
  67 + },
  68 + resetQuery() {
  69 + this.page.num = 1;
  70 + this.$nextTick(() => {
  71 + this.$refs.alarmPopupRef.resetQuery()
  72 + })
  73 + this.conditions = {}
  74 + },
  75 + downCallback() {
  76 + this.list = [];
  77 + this.page.num = 1;
  78 + this.resetQuery();
  79 + this.loadData(this.page.num, {
  80 + deviceId: this.deviceId
  81 + });
  82 + },
  83 + upCallback() {
  84 + const deviceId = {
  85 + deviceId: this.deviceId
  86 + }
  87 + const condition = Object.values(this.conditions)
  88 + if (condition.length === 0) {
  89 + this.page.num += 1;
  90 + this.loadData(this.page.num, {
  91 + ...deviceId
  92 + });
  93 + } else if (condition.filter(Boolean).length > 0) {
  94 + this.page.num += 1;
  95 + this.loadData(this.page.num, {
  96 + ...this.conditions,
  97 + ...deviceId
  98 + });
  99 + } else {
  100 + this.page.num += 1;
  101 + this.loadData(this.page.num, {
  102 + ...deviceId
  103 + });
  104 + }
  105 + },
  106 + async loadData(page, param) {
  107 + let that = this;
  108 + let params = {
  109 + page,
  110 + pageSize: 10,
  111 + ...param
  112 + };
  113 + const res = await api.alarmApi.getAlarmApi({
  114 + params,
  115 + custom: {
  116 + load: false
  117 + }
  118 + })
  119 + if (!res) return
  120 + uni.stopPullDownRefresh();
  121 + that.mescroll.endByPage(res.items.length, res.total); //必传参数(当前页的数据个数, 总页数)
  122 + that.alarmTotal = res.total;
  123 + if (page == 1) {
  124 + that.list = res.items;
  125 + } else {
  126 + that.list = that.list.concat(res.items);
  127 + }
  128 + },
  129 + openSearchDialog() {
  130 + this.show = true;
  131 + this.resetQuery();
  132 + },
  133 + close() {
  134 + this.show = false;
  135 + },
  136 + openAlertDetail(e) {
  137 + useNavigateTo('/alarm-subpackage/alarm-detail/alarm-detail?data=', e)
  138 + }
  139 + }
  140 + };
  141 +</script>
  142 +
  143 +<style lang="scss" scoped>
  144 + .filter-button {
  145 + margin: 0 20rpx;
  146 + font-size: 12px;
  147 + width: 160rpx;
  148 + height: 64rpx;
  149 + border-radius: 32rpx;
  150 + display: flex;
  151 + justify-content: center;
  152 + align-items: center;
  153 + background: #f0f1f2;
  154 + color: #666;
  155 +
  156 + image {
  157 + width: 28rpx;
  158 + height: 28rpx;
  159 + margin-left: 4rpx;
  160 + }
  161 + }
  162 +</style>
\ No newline at end of file
... ...
device-subpackage/device-detail/components/basic-info.vue renamed from deviceSubPage/deviceDetailPage/tabDetail/basicInfo.vue
1   -<template>
2   - <view class="basic-page">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module />
5   - <view class="basic-title">
6   - <view class="u-flex">
7   - <view style="padding-left: 32rpx;">
8   - <u-icon v-if="deviceDetail.deviceInfo.longitude !== ''" @click="handleClick" name="map-fill">
9   - </u-icon>
10   - </view>
11   - <view class="text-clip" style="margin-left: 20rpx;width:370rpx">
12   - {{ deviceDetail.alias? deviceDetail.alias: deviceDetail.name }}
13   - </view>
14   - <view style="margin-left: 20rpx; font-size: 14px;"
15   - :style="{ color: deviceDetail.deviceState === 'INACTIVE' ? '#666' : deviceDetail.deviceState === 'ONLINE' ? '#377DFF' : '#DE4437' }">
16   - {{ deviceStatus }}
17   - </view>
18   - </view>
19   - <view style="margin-right: 20rpx;"
20   - v-if="deviceDetail.deviceState === 'ONLINE' && deviceDetail.deviceType !== 'SENSOR'">
21   - <!-- #ifdef MP -->
22   - <u-button type="primary" shape="circle" size="mini" text="下发命令" @click="showModalBtn" />
23   - <!-- #endif -->
24   - <!-- #ifdef APP-PLUS -->
25   - <view class="cu-item" @tap="showModal" data-target="Modal">
26   - <text>下发命令</text>
27   - </view>
28   - <!-- #endif -->
29   - </view>
30   - </view>
31   - <view class="detail">
32   - <view class="detail-item">
33   - <view class="detail-label">设备编号</view>
34   - <view class="detail-value">{{ deviceDetail.sn }}</view>
35   - </view>
36   - <u-line length="90%" margin="0 auto"></u-line>
37   - <view class="detail-item">
38   - <view class="detail-label">设备类型</view>
39   - <view class="detail-value">{{ deviceType }}</view>
40   - </view>
41   - <u-line length="90%" margin="0 auto"></u-line>
42   - <view class="detail-item">
43   - <view class="detail-label">所属组织</view>
44   - <view class="detail-value">{{ deviceDetail.organizationDTO.name }}</view>
45   - </view>
46   - <u-line length="90%" margin="0 auto"></u-line>
47   - <view class="detail-item">
48   - <view class="detail-label">最后连接时间</view>
49   - <view class="detail-value">{{ formatLastOnlineTime }}</view>
50   - </view>
51   - <u-line length="90%" margin="0 auto"></u-line>
52   - <view class="detail-item">
53   - <view class="detail-label">是否告警</view>
54   - <view class="detail-value">{{ alarmStatus }}</view>
55   - </view>
56   - <u-line length="90%" margin="0 auto"></u-line>
57   - <view class="detail-item">
58   - <view class="detail-label">设备描述</view>
59   - <view class="detail-value">{{ deviceDetail.description }}</view>
60   - </view>
61   - </view>
62   - <!-- 下发命令 -->
63   - <!-- #ifdef APP-PLUS -->
64   - <view v-show="showNodal" class="cu-modal" :class="modalName=='Modal'?'show':''">
65   - <view class="cu-dialog" style="">
66   - <view class="modal_main">
67   - <view class='nav-list margin-top'>
68   - <view style="width: 100%; padding: 0 30rpx;">
69   - <view style="text-align: center; font-weight:700;margin-top: 40rpx;">命令下发</view>
70   - <view style="height: 20rpx;"></view>
71   - <view class="u-flex">
72   - <text
73   - style="color: #333; font-size: 14px;font-weight:700;margin-right: 30rpx;">下发类型:</text>
74   - <view>
75   - <radio-group style="display: flex;" @change="radioChange">
76   - <label class="uni-list-cell uni-list-cell-pd" v-for="(item, index) in items"
77   - :key="item.value">
78   - <view style="display: flex">
79   - <view style="margin-left: 10rpx;">
80   - <radio :value="item.value" :checked="index === current" />
81   - </view>
82   - <view style="width:10rpx"></view>
83   - <view style="margin-left: 10rpx;">{{item.name}}</view>
84   - </view>
85   - </label>
86   - </radio-group>
87   - </view>
88   - </view>
89   - <view style="margin-top: 15rpx">
90   - <view class="cusAppplusContent">
91   - <textarea v-model="inputCommandVal" placeholder="请输入下发内容(json格式)" />
92   - </view>
93   - </view>
94   - <view class="button-group">
95   - <view>
96   - <view class="cusAppplusCancelBtn" @click="cancelCommand"><text
97   - style="color: #333333">取消</text>
98   - </view>
99   - </view>
100   - <view>
101   - <view class="cusAppplusConfrimBtn" @click="confirmCommand">
102   - <text style="color:white">确认</text>
103   - </view>
104   - </view>
105   - </view>
106   - <view style="height:30rpx"></view>
107   - </view>
108   - </view>
109   - </view>
110   - </view>
111   - </view>
112   - <!-- #endif -->
113   - <!-- #ifdef MP -->
114   - <u-modal :show="showModel" closeOnClickOverlay :showConfirmButton="false" width="720rpx" @close="hiddenModal"
115   - @touchmove.stop.prevent="disabledScroll">
116   - <view style="width: 100%; padding: 0 30rpx;">
117   - <view style="text-align: center; font-weight:700;margin-bottom: 40rpx;">命令下发</view>
118   - <view class="u-flex">
119   - <text style="color: #333; font-size: 14px;font-weight:700;margin-right: 30rpx;">下发类型:</text>
120   -
121   - <u-radio-group v-model="commandType" placement="row">
122   - <u-radio activeColor="#3388FF" label="单向" name="OneWay"></u-radio>
123   - <view style="margin: 0 20rpx;"></view>
124   - <u-radio activeColor="#3388FF" label="双向" name="TwoWay"></u-radio>
125   - </u-radio-group>
126   - </view>
127   - <view style="margin-top: 28rpx;width: 100%;">
128   - <div class="u-flex u-row-between">
129   - <u--textarea :placeholder="`请输入下发内容${isShowTCP?'(字符串格式)':'(json格式)'}`" v-model="inputCommandVal" />
130   - <u-icon v-if="!isShowTCP" @click="handleCopy(copyTextValue)" name="question-circle" color="#2979ff" size="28"
131   - class="ml-10">
132   - </u-icon>
133   - </div>
134   - </view>
135   -
136   - <view class="button-group">
137   - <view>
138   - <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="取消"
139   - @click="cancelCommand"></u-button>
140   - </view>
141   - <view>
142   - <u-button color="#3388ff" shape="circle" text="确认" @click="confirmCommand"></u-button>
143   - </view>
144   - </view>
145   - </view>
146   - </u-modal>
147   - <!-- #endif -->
148   - </view>
149   -</template>
150   -
151   -<script>
152   - import {
153   - formatToDate
154   - } from '@/plugins/utils.js';
155   - import {
156   - issueCommand
157   - } from '../api/index.js';
158   - export default {
159   - props: {
160   - deviceDetail: {
161   - type: Object,
162   - default: () => ({})
163   - }
164   - },
165   - data() {
166   - return {
167   - copyTextValue: {
168   - "method": "methodThingskit",
169   - "params": {
170   - "pin": 7,
171   - "value": 1
172   - }
173   - },
174   - showNodal: false,
175   - items: [{
176   - value: 'OneWay',
177   - name: '单向',
178   - checked: 'true'
179   - },
180   - {
181   - value: 'TwoWay',
182   - name: '双向'
183   - },
184   - ],
185   - current: 0,
186   - modalName: null,
187   - showModel: false,
188   - commandType: 'OneWay',
189   - commandValue: {},
190   - inputCommandVal: '',
191   - isShowTCP:false,//用于下发命令时判断是否是TCP/UDP
192   - };
193   - },
194   - computed: {
195   - deviceStatus(){
196   - return this.deviceDetail.deviceState === 'INACTIVE' ? '待激活' : this.deviceDetail.deviceState === 'ONLINE' ? '在线' : '离线'
197   - },
198   - deviceType() {
199   - return this.deviceDetail.deviceType === 'DIRECT_CONNECTION' ?
200   - '直连设备' :
201   - this.deviceDetail.deviceType === 'GATEWAY' ?
202   - '网关设备' :
203   - this.deviceDetail.deviceType === 'SENSOR' ?
204   - '网关子设备' :
205   - '';
206   - },
207   - alarmStatus() {
208   - return this.deviceDetail.alarmStatus === '0' ? '否' : '是';
209   - },
210   - formatLastOnlineTime() {
211   - return formatToDate(Number(this.deviceDetail.lastOnlineTime), 'YYYY-MM-DD HH:mm:ss');
212   - }
213   - },
214   - onLoad(e) {
215   - // 隐藏原生的tabbar
216   - uni.hideTabBar();
217   - },
218   - mounted() {},
219   - beforeCreate() {
220   - this.modalName = null
221   - },
222   - methods: {
223   - handleCopy(value) {
224   - uni.showModal({
225   - content: JSON.stringify(value),
226   - confirmText: '复制内容',
227   - showCancel:false,
228   - success: () => {
229   - uni.setClipboardData({
230   - data: JSON.stringify(value),
231   - success: () => {
232   - uni.showToast({
233   - title: '复制成功'
234   - })
235   - }
236   - });
237   - },
238   -
239   - });
240   - },
241   - radioChange: function(evt) {
242   - for (let i = 0; i < this.items.length; i++) {
243   - if (this.items[i].value === evt.detail.value) {
244   - this.current = i;
245   - break;
246   - }
247   - }
248   - this.commandType = evt.detail.value
249   - },
250   - handleClick() {
251   - const data = {
252   - longitude: this.deviceDetail.deviceInfo.longitude || 0,
253   - latitude: this.deviceDetail.deviceInfo.latitude || 0
254   - };
255   - uni.navigateTo({
256   - url: '/deviceSubPage/deviceDetailPage/devicePosition?data=' + JSON.stringify(data)
257   - });
258   - },
259   - showModal(e) {
260   - this.modalName = e.currentTarget.dataset.target
261   - const {transportType} = this.deviceDetail.deviceProfile
262   - this.isShowTCP = transportType=='TCP'?true:false
263   - this.showNodal = true
264   - },
265   - showModalBtn() {
266   - this.showModel = true;
267   - this.inputCommandVal = '';
268   - const {transportType} = this.deviceDetail.deviceProfile
269   - this.isShowTCP = transportType=='TCP'?true:false
270   - },
271   - disabledScroll() {
272   - return;
273   - },
274   - hiddenModal() {
275   - this.showModel = false;
276   - this.inputCommandVal = '';
277   - // #ifdef APP-PLUS
278   - this.modalName = null
279   - this.showNodal = false
280   - // #endif
281   - },
282   - async confirmCommand() {
283   - try {
284   - this.commandValue.method = 'methodThingskit';
285   - this.commandValue.persistent = true;
286   - this.commandValue.additionalInfo = {
287   - cmdType: 'API'
288   - };
289   - if(this.isShowTCP){//TCP的格式只能是字符串
290   - const zg = /^[0-9a-zA-Z]*$/
291   - if(!zg.test(this.inputCommandVal)) {
292   - uni.$u.toast('输入的内容只能是字母和数字的组合')
293   - return
294   - }
295   - this.commandValue.params = this.inputCommandVal
296   - }else{
297   - const commandJsonValue = JSON.parse(this.inputCommandVal);
298   - this.commandValue.params = commandJsonValue
299   - }
300   - await issueCommand(this.commandType, this.deviceDetail.tbDeviceId, this.commandValue);
301   - this.hiddenModal();
302   - uni.$u.toast('下发成功~');
303   - } catch (e) {
304   - uni.$u.toast('下发失败~');
305   - }
306   - },
307   - cancelCommand() {
308   - this.hiddenModal();
309   - // #ifdef APP-PLUS
310   - this.modalName = null
311   - this.showNodal = false
312   - // #endif
313   - }
314   - }
315   - };
316   -</script>
317   -
318   -<style lang="scss" scoped>
319   - @import url('../styles/modal.css');
320   -
321   - .cusAppplusContent {
322   - width: 625rpx;
323   - height: 400rpx;
324   - background: #FFFFFF;
325   - box-shadow: 2px 2px 4px 0px rgba(0, 0, 0, 0.03);
326   - border-radius: 10px;
327   -
328   - }
329   -
330   - .cusAppplusCancelBtn {
331   - background: #e3e3e5;
332   - border-radius: 38rpx;
333   - height: 85rpx;
334   - line-height: 85rpx
335   - }
336   -
337   - .cusAppplusConfrimBtn {
338   - background: #3388ff;
339   - border-radius: 38rpx;
340   - height: 85rpx;
341   - line-height: 85rpx
342   - }
343   -
344   - .cu-item {
345   - background: #3388FF;
346   - border-radius: 12px;
347   - width: 120rpx;
348   - height: 48rpx;
349   - text-align: center;
350   - line-height: 40rpx;
351   -
352   - text {
353   - font-size: 12px;
354   - font-family: PingFangSC-Regular, PingFang SC;
355   - font-weight: 400;
356   - color: #FFFFFF;
357   - }
358   - }
359   -
360   -
361   -
362   - .basic-page {
363   - padding: 0 30rpx;
364   -
365   - .basic-title {
366   - display: flex;
367   - justify-content: space-between;
368   - align-items: center;
369   - height: 140rpx;
370   - background-color: #fff;
371   - border-radius: 20rpx;
372   - }
373   -
374   - .detail {
375   - background-color: #fff;
376   - margin-top: 30rpx;
377   - border-radius: 20rpx;
378   - width: 690rpx;
379   -
380   - .detail-item {
381   - padding: 30rpx;
382   - display: flex;
383   - align-items: center;
384   -
385   - .detail-label {
386   - color: #333;
387   - font-size: 15px;
388   - }
389   -
390   - .detail-value {
391   - color: #666;
392   - font-size: 14px;
393   - margin-left: 30rpx;
394   - }
395   - }
396   - }
397   - }
398   -
399   - /deep/ .u-modal__content {
400   - padding: 30rpx 0 !important;
401   - }
402   -
403   - .button-group {
404   - display: flex;
405   - margin-top: 40rpx;
406   - justify-content: space-between;
407   -
408   - view {
409   - width: 300rpx;
410   - }
411   - }
412   -</style>
  1 +<template>
  2 + <view class="basic-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <!-- 基础信息头部 -->
  6 + <view class="basic-header">
  7 + <view class="u-flex">
  8 + <view class="pl-3">
  9 + <u-icon v-if="deviceDetail.deviceInfo.longitude !== ''" @click="handleClick"
  10 + name="map-fill"></u-icon>
  11 + </view>
  12 + <view class="basic-text text-clip ml-2">
  13 + {{ deviceDetail.alias ? deviceDetail.alias : deviceDetail.name }}
  14 + </view>
  15 + <view class="basic-text-status ml-2" :style="{ color: formatTextStatus(deviceDetail.deviceState) }">
  16 + {{ deviceStatus }}
  17 + </view>
  18 + </view>
  19 + <!-- 命令下发 设备在线并且不是网关子设备 -->
  20 + <view class="mr-2" v-if="deviceDetail.deviceState === 'ONLINE' && deviceDetail.deviceType !== 'SENSOR'">
  21 + <!-- #ifdef MP -->
  22 + <u-button type="primary" shape="circle" size="mini" text="下发命令" @click="handleMpShowModal" />
  23 + <!-- #endif -->
  24 + <!-- #ifdef APP-PLUS -->
  25 + <view class="cu-item" @tap="handleAppShowModal" data-target="Modal">
  26 + <text>下发命令</text>
  27 + </view>
  28 + <!-- #endif -->
  29 + </view>
  30 + </view>
  31 + <!-- 设备详情 -->
  32 + <view class="detail">
  33 + <view class="detail-item">
  34 + <view class="detail-label">设备编号</view>
  35 + <view class="detail-value">{{ deviceDetail.sn }}</view>
  36 + </view>
  37 + <u-line length="90%" margin="0 auto"></u-line>
  38 + <view class="detail-item">
  39 + <view class="detail-label">设备类型</view>
  40 + <view class="detail-value">{{ deviceType }}</view>
  41 + </view>
  42 + <u-line length="90%" margin="0 auto"></u-line>
  43 + <view class="detail-item">
  44 + <view class="detail-label">所属组织</view>
  45 + <view class="detail-value">{{ deviceDetail.organizationDTO.name }}</view>
  46 + </view>
  47 + <u-line length="90%" margin="0 auto"></u-line>
  48 + <view class="detail-item">
  49 + <view class="detail-label">最后连接时间</view>
  50 + <view class="detail-value">{{ formatLastOnlineTime }}</view>
  51 + </view>
  52 + <u-line length="90%" margin="0 auto"></u-line>
  53 + <view class="detail-item">
  54 + <view class="detail-label">是否告警</view>
  55 + <view class="detail-value">{{ alarmStatus }}</view>
  56 + </view>
  57 + <u-line length="90%" margin="0 auto"></u-line>
  58 + <view class="detail-item">
  59 + <view class="detail-label">设备描述</view>
  60 + <view class="detail-value">{{ deviceDetail.description }}</view>
  61 + </view>
  62 + </view>
  63 + <!-- 命令下发 -->
  64 + <!-- #ifdef APP-PLUS -->
  65 + <!-- 原生弹窗 封装成子组件无效 -->
  66 + <view v-show="showNativeModal" class="cu-modal" :class="modalName == 'Modal' ? 'show' : ''">
  67 + <view class="cu-dialog">
  68 + <view class="app-command-content">
  69 + <view class="app-command-text">
  70 + <text>命令下发</text>
  71 + </view>
  72 + <view class="app-command-type">
  73 + <text>下发类型</text>
  74 + <view class="mr-2">
  75 + <radio-group @change="radioChange" class="flex mr-1">
  76 + <label v-for="(item, index) in commandTypeList" :key="item.value">
  77 + <view class="flex">
  78 + <view class="ml-1">
  79 + <radio :value="item.value" :checked="index === current" />
  80 + </view>
  81 + <view style="width:10rpx"></view>
  82 + <view class="ml-1">{{item.name}}</view>
  83 + </view>
  84 + </label>
  85 + </radio-group>
  86 + </view>
  87 + </view>
  88 + <view class="app-command-body">
  89 + <textarea class="app-command-textarea" v-model="inputCommandContent"
  90 + :placeholder="`请输入下发内容${isShowTCP?'(字符串格式)':'(json格式)'}`" />
  91 + <u-icon @click="handleCopy(copyTextValue)" v-if="!isShowTCP" name="question-circle"
  92 + color="#2979ff" size="28" class="ml-10">
  93 + </u-icon>
  94 + </view>
  95 + <view class="app-command-buttons">
  96 + <view class="cancel-button" @click="cancelCommand"><text class="cancel-text">取消</text></view>
  97 + <view @click="handleAppCommand" class="confrim-button"><text class="confrim-text">确认</text>
  98 + </view>
  99 + </view>
  100 + </view>
  101 + </view>
  102 + </view>
  103 + <!-- #endif -->
  104 + <!-- #ifdef MP -->
  105 + <!-- u-modal在app端弹窗层级无法覆盖背景色 -->
  106 + <mp-command-issuance ref="mpCommandIssuanceRef" :isShowTCP="isShowTCP" :showModal="mpShowModal"
  107 + @hideModal="hideMpModal" @cancelCommand="cancelCommand"
  108 + @confirmCommand="confirmCommand"></mp-command-issuance>
  109 + <!-- #endif -->
  110 + </view>
  111 +</template>
  112 +
  113 +<script>
  114 + import {formatToDate} from '@/plugins/utils.js';
  115 + import {issueCommand} from '../api/index.js';
  116 + import api from '@/api/index.js';
  117 + import mpCommandIssuance from './mp-command-issuance.vue';
  118 + import {commandTypeList} from '../config/data.js'
  119 + import {useShowModal} from '@/plugins/utils.js'
  120 +
  121 + export default {
  122 + components: {
  123 + mpCommandIssuance,
  124 + },
  125 + props: {
  126 + deviceDetail: {
  127 + type: Object,
  128 + default: () => ({})
  129 + }
  130 + },
  131 + data() {
  132 + return {
  133 + showNativeModal: false,
  134 + current: 0,
  135 + commandTypeList,
  136 + commandTypeStr: 'OneWay',
  137 + inputCommandContent: '',
  138 + mpShowModal: false,
  139 + commandValue: {},
  140 + isShowTCP: false, //用于下发命令时判断是否是TCP/UDP
  141 + modalName: null,
  142 + copyTextValue: {
  143 + "method": "methodThingskit",
  144 + "params": {
  145 + "pin": 7,
  146 + "value": 1
  147 + }
  148 + }
  149 + };
  150 + },
  151 + computed: {
  152 + deviceStatus() {
  153 + return this.deviceDetail.deviceState === 'INACTIVE' ? '待激活' : this.deviceDetail.deviceState === 'ONLINE' ?
  154 + '在线' : '离线';
  155 + },
  156 + deviceType() {
  157 + return this.deviceDetail.deviceType === 'DIRECT_CONNECTION' ?
  158 + '直连设备' :
  159 + this.deviceDetail.deviceType === 'GATEWAY' ?
  160 + '网关设备' :
  161 + this.deviceDetail.deviceType === 'SENSOR' ?
  162 + '网关子设备' :
  163 + '';
  164 + },
  165 + alarmStatus() {
  166 + return this.deviceDetail.alarmStatus === '0' ? '否' : '是';
  167 + },
  168 + formatLastOnlineTime() {
  169 + return formatToDate(Number(this.deviceDetail.lastOnlineTime), 'YYYY-MM-DD HH:mm:ss');
  170 + }
  171 + },
  172 + beforeCreate() {
  173 + this.modalName = null
  174 + },
  175 + onLoad() {
  176 + // 隐藏原生的tabbar
  177 + uni.hideTabBar();
  178 + this.modalName = null
  179 + },
  180 + methods: {
  181 + handleCopy(value) {
  182 + useShowModal(JSON.stringify(value), '命令下发', '复制内容').then(res => {
  183 + uni.setClipboardData({
  184 + data: JSON.stringify(value),
  185 + success: () => {
  186 + uni.showToast({
  187 + title: '复制成功'
  188 + })
  189 + }
  190 + });
  191 + })
  192 + },
  193 + radioChange: function(evt) {
  194 + for (let i = 0; i < this.commandTypeList.length; i++) {
  195 + if (this.items[i].value === evt.detail.value) {
  196 + this.current = i;
  197 + break;
  198 + }
  199 + }
  200 + this.commandTypeStr = evt.detail.value
  201 + },
  202 + formatTextStatus(deviceState) {
  203 + return deviceState === 'INACTIVE' ? '#666' : deviceState === 'ONLINE' ? '#377DFF' : '#DE4437';
  204 + },
  205 + handleClick() {
  206 + const data = {
  207 + longitude: this.deviceDetail.deviceInfo.longitude || 0,
  208 + latitude: this.deviceDetail.deviceInfo.latitude || 0
  209 + };
  210 + uni.navigateTo({
  211 + url: '/device-subpackage/device-detail/device-position?data=' + JSON.stringify(data)
  212 + });
  213 + },
  214 + disabledScroll() {
  215 + return;
  216 + },
  217 + handleAppShowModal(e) {
  218 + this.modalName = e.currentTarget.dataset.target;
  219 + this.showNativeModal = true
  220 + },
  221 + handleMpShowModal() {
  222 + const {
  223 + transportType
  224 + } = this.deviceDetail.deviceProfile;
  225 + this.isShowTCP = transportType == 'TCP' ? true : false;
  226 + this.mpShowModal = true;
  227 + },
  228 + hideMpModal() {
  229 + this.mpShowModal = false;
  230 + },
  231 + hideAppModal() {
  232 + this.modalName = null;
  233 + this.showNativeModal = false
  234 + },
  235 + confirmCommand(commandType, inputCommandVal) {
  236 + this.handleCommand(commandType, inputCommandVal)
  237 + },
  238 + cancelCommand() {
  239 + this.hideMpModal();
  240 + this.hideAppModal();
  241 + this.commandTypeStr = 'OneWay'
  242 + this.inputCommandContent = ''
  243 + this.$nextTick(() => {
  244 + this.$refs.mpCommandIssuanceRef.reset()
  245 + })
  246 + },
  247 + handleAppCommand() {
  248 + this.handleCommand(this.commandTypeStr, this.inputCommandContent)
  249 + },
  250 + async handleCommand(commandType, inputCommandVal) {
  251 + if (!inputCommandVal) return uni.$u.toast('请输入下发内容~');
  252 + if (this.isShowTCP) {
  253 + //TCP的格式只能是字符串
  254 + const zg = /^[0-9a-zA-Z]*$/;
  255 + if (!zg.test(inputCommandVal)) {
  256 + uni.$u.toast('输入的内容只能是字母和数字的组合');
  257 + return;
  258 + }
  259 + this.commandValue.params = inputCommandVal;
  260 + } else {
  261 + this.commandValue.params = JSON.parse(inputCommandVal);
  262 + }
  263 + this.commandValue.persistent = true;
  264 + this.commandValue.additionalInfo = {
  265 + cmdType: 'API'
  266 + };
  267 + this.commandValue.method = 'methodThingskit';
  268 + this.commandValue.params = inputCommandVal;
  269 + await api.deviceApi.issueCommand(commandType, this.deviceDetail.tbDeviceId, this.commandValue);
  270 + this.cancelCommand();
  271 + uni.$u.toast('下发成功~');
  272 +
  273 + },
  274 +
  275 + }
  276 + };
  277 +</script>
  278 +
  279 +<style lang="scss" scoped>
  280 + @import url('../static/modal.css');
  281 +
  282 + .app-command-content {
  283 + .app-command-text {
  284 + display: flex;
  285 + justify-content: center;
  286 + align-items: center;
  287 +
  288 + text {
  289 + font-weight: 700;
  290 + }
  291 + }
  292 +
  293 + .app-command-type {
  294 + display: flex;
  295 + margin-top: 20rpx;
  296 + margin-left: 20rpx;
  297 +
  298 + text {
  299 + font-weight: 700;
  300 + }
  301 + }
  302 +
  303 + .app-command-body {
  304 + display: flex;
  305 + align-items: center;
  306 + justify-content: space-between;
  307 + margin-top: 20rpx;
  308 + margin-left: 20rpx;
  309 +
  310 + .app-command-textarea {
  311 + width: 625rpx;
  312 + height: 400rpx;
  313 + background: #FFFFFF;
  314 + box-shadow: 2px 2px 4px 0px rgba(0, 0, 0, 0.03);
  315 + border-radius: 10px;
  316 + }
  317 + }
  318 +
  319 + .app-command-buttons {
  320 + display: flex;
  321 + align-items: center;
  322 + justify-content: space-evenly;
  323 + height: 85rpx;
  324 + margin-top: 20rpx;
  325 + margin-bottom: 20rpx;
  326 +
  327 + .cancel-button {
  328 + width: 300rpx;
  329 + background: #e3e3e5;
  330 + height: 85rpx;
  331 + border-radius: 38rpx;
  332 + line-height: 85rpx;
  333 +
  334 + .cancel-text {
  335 + color: #333333
  336 + }
  337 + }
  338 +
  339 + .confrim-button {
  340 + width: 300rpx;
  341 + background: #3388ff;
  342 + border-radius: 38rpx;
  343 + height: 85rpx;
  344 + line-height: 85rpx;
  345 +
  346 + .confrim-text {
  347 + color: white
  348 + }
  349 + }
  350 + }
  351 + }
  352 +
  353 + .basic-page {
  354 + padding: 0 30rpx;
  355 +
  356 + .basic-header {
  357 + display: flex;
  358 + justify-content: space-between;
  359 + align-items: center;
  360 + height: 140rpx;
  361 + background-color: #fff;
  362 + border-radius: 20rpx;
  363 +
  364 + .basic-text {
  365 + width: 370rpx;
  366 + }
  367 +
  368 + .cu-item {
  369 + background: #3388ff;
  370 + border-radius: 12px;
  371 + width: 120rpx;
  372 + height: 48rpx;
  373 + text-align: center;
  374 + line-height: 40rpx;
  375 +
  376 + text {
  377 + font-size: 12px;
  378 + font-family: PingFangSC-Regular, PingFang SC;
  379 + font-weight: 400;
  380 + color: #ffffff;
  381 + }
  382 + }
  383 +
  384 + .basic-text-status {
  385 + font-size: 14px;
  386 + }
  387 + }
  388 +
  389 + .detail {
  390 + background-color: #fff;
  391 + margin-top: 30rpx;
  392 + border-radius: 20rpx;
  393 + width: 690rpx;
  394 +
  395 + .detail-item {
  396 + padding: 30rpx;
  397 + display: flex;
  398 + align-items: center;
  399 +
  400 + .detail-label {
  401 + color: #333;
  402 + font-size: 15px;
  403 + }
  404 +
  405 + .detail-value {
  406 + color: #666;
  407 + font-size: 14px;
  408 + margin-left: 30rpx;
  409 + }
  410 + }
  411 + }
  412 + }
  413 +
  414 + /deep/ .u-modal__content {
  415 + padding: 30rpx 0 !important;
  416 + }
  417 +</style>
\ No newline at end of file
... ...
device-subpackage/device-detail/components/command-detail.vue renamed from deviceSubPage/deviceDetailPage/tabDetail/CommandDetail.vue
1   -<template>
2   - <view class="command-detail">
3   - <view class="detail-top">{{ commandDetail.deviceName }}</view>
4   - <view class="detail">
5   - <view class="detail-item">
6   - <view class="detail-label">设备类型</view>
7   - <view class="detail-value">{{ deviceType }}</view>
8   - </view>
9   - <u-line length="90%" margin="0 auto"></u-line>
10   - <view class="detail-item">
11   - <view class="detail-label">设备编号</view>
12   - <view class="detail-value">{{ commandDetail.deviceSn }}</view>
13   - </view>
14   - <u-line length="90%" margin="0 auto"></u-line>
15   - <view class="detail-item">
16   - <view class="detail-label">所属组织</view>
17   - <view class="detail-value">{{ commandDetail.organizationName }}</view>
18   - </view>
19   - <u-line length="90%" margin="0 auto"></u-line>
20   - <view class="detail-item">
21   - <view class="detail-label">命令下发时间</view>
22   - <view class="detail-value">{{ format(commandDetail.createTime) }}</view>
23   - </view>
24   - <u-line length="90%" margin="0 auto"></u-line>
25   - <view class="detail-item">
26   - <view class="detail-label">命令类型</view>
27   - <view class="detail-value">{{ commandDetail.additionalInfo.cmdType===1?'服务':'自定义' }}</view>
28   - </view>
29   - <u-line length="90%" margin="0 auto" v-if="commandDetail.additionalInfo.cmdType"></u-line>
30   - <view class="detail-item">
31   - <view class="detail-label">响应类型</view>
32   - <view class="detail-value">{{ commandDetail.request.oneway ? '单向' : '双向' }}</view>
33   - </view>
34   - <u-line length="90%" margin="0 auto"></u-line>
35   - <view class="detail-item">
36   - <view class="detail-label">命令状态</view>
37   - <view class="detail-value">{{ commandDetail.statusName }}</view>
38   - </view>
39   - <u-line length="90%" margin="0 auto"></u-line>
40   - <view class="detail-item" v-if="!commandDetail.request.oneway">
41   - <view class="detail-label">响应结果</view>
42   - <view class="detail-value">{{ commandDetail.response?JSON.stringify(commandDetail.response):'无' }}</view>
43   - </view>
44   - <!-- <view class="detail-item" v-if="!commandDetail.request.oneway">
45   - <view class="detail-label">响应失败内容</view>
46   - <view class="detail-value" style="width: 400rpx;" v-if="commandDetail.response.status!=='SUCCESS'">
47   - <u--textarea placeholder="响应失败内容" v-model="failContent" />
48   - </view>
49   - </view> -->
50   - </view>
51   - <view class="command">命令内容</view>
52   - <u-textarea :value="formatValue(commandDetail.request.body)" disabled></u-textarea>
53   - <view style="height: 50rpx;"></view>
54   - </view>
55   -</template>
56   -
57   -<script>
58   - import {
59   - formatToDate
60   - } from '@/plugins/utils.js';
61   - export default {
62   - data() {
63   - return {
64   - commandDetail: {},
65   - failContent: ""
66   - };
67   - },
68   - computed: {
69   - deviceType() {
70   - return this.commandDetail.deviceType === 'DIRECT_CONNECTION' ?
71   - '直连设备' :
72   - this.commandDetail.deviceType === 'GATEWAY' ?
73   - '网关设备' :
74   - this.commandDetail.deviceType === 'SENSOR' ?
75   - '网关子设备' :
76   - '';
77   - }
78   - },
79   - methods: {
80   - format(date) {
81   - return formatToDate(date, 'YYYY-MM-DD HH:mm:ss');
82   - },
83   - formatValue(value) {
84   - try {
85   - const val = JSON.parse(value['params']);
86   - //微信小程序端object无法显示,格式化为字符串
87   - const stringifyVal = JSON.stringify(val['params'])
88   - const formatVal = stringifyVal
89   - .replace(/\\"/g, '"')
90   - .replace(/]"/g, ']')
91   - .replace(/"\[/g, '[');
92   - return formatVal
93   - } catch (e) {
94   - console.error("命令记录页面格式化无返回值", e);
95   - return value['params']
96   - }
97   - }
98   - },
99   - onLoad(options) {
100   - const {
101   - data
102   - } = options;
103   - this.commandDetail = JSON.parse(data);
104   - if (this.commandDetail.response.status === 'SUCCESS') return
105   - this.failContent = JSON.stringify(this.commandDetail.response.error)
106   - }
107   - };
108   -</script>
109   -
110   -<style lang="scss" scoped>
111   - .command-detail {
112   - padding: 0 30rpx;
113   - height: 100vh;
114   - background-color: #f8f9fa;
115   -
116   - .detail-top {
117   - height: 118rpx;
118   - width: 690rpx;
119   - display: flex;
120   - align-items: center;
121   - background-color: #fff;
122   - color: #333;
123   - border-radius: 20rpx;
124   - font-size: 15px;
125   - margin-top: 30rpx;
126   - padding: 30rpx;
127   - }
128   -
129   - .detail {
130   - background-color: #fff;
131   - margin-top: 30rpx;
132   - border-radius: 20rpx;
133   - width: 690rpx;
134   -
135   - .detail-item {
136   - padding: 30rpx;
137   - display: flex;
138   - align-items: center;
139   -
140   - .detail-label {
141   - color: #333;
142   - font-size: 15px;
143   - }
144   -
145   - .detail-value {
146   - color: #666;
147   - font-size: 14px;
148   - margin-left: 30rpx;
149   - }
150   - }
151   - }
152   -
153   - .command {
154   - margin: 30rpx 0;
155   - }
156   - }
157   -</style>
  1 +<template>
  2 + <view class="command-detail">
  3 + <view class="detail-top">{{ commandDetail.deviceName }}</view>
  4 + <view class="detail">
  5 + <view class="detail-item">
  6 + <view class="detail-label">设备类型</view>
  7 + <view class="detail-value">{{ deviceType }}</view>
  8 + </view>
  9 + <u-line length="90%" margin="0 auto"></u-line>
  10 + <view class="detail-item">
  11 + <view class="detail-label">设备编号</view>
  12 + <view class="detail-value">{{ commandDetail.deviceSn }}</view>
  13 + </view>
  14 + <u-line length="90%" margin="0 auto"></u-line>
  15 + <view class="detail-item">
  16 + <view class="detail-label">所属组织</view>
  17 + <view class="detail-value">{{ commandDetail.organizationName }}</view>
  18 + </view>
  19 + <u-line length="90%" margin="0 auto"></u-line>
  20 + <view class="detail-item">
  21 + <view class="detail-label">命令下发时间</view>
  22 + <view class="detail-value">{{ format(commandDetail.createTime) }}</view>
  23 + </view>
  24 + <u-line length="90%" margin="0 auto"></u-line>
  25 + <view class="detail-item">
  26 + <view class="detail-label">命令类型</view>
  27 + <view class="detail-value">{{ commandDetail.additionalInfo.cmdType===1?'服务':'自定义' }}</view>
  28 + </view>
  29 + <u-line length="90%" margin="0 auto" v-if="commandDetail.additionalInfo.cmdType"></u-line>
  30 + <view class="detail-item">
  31 + <view class="detail-label">响应类型</view>
  32 + <view class="detail-value">{{ commandDetail.request.oneway ? '单向' : '双向' }}</view>
  33 + </view>
  34 + <u-line length="90%" margin="0 auto"></u-line>
  35 + <view class="detail-item">
  36 + <view class="detail-label">命令状态</view>
  37 + <view class="detail-value">{{ commandDetail.statusName }}</view>
  38 + </view>
  39 + <u-line length="90%" margin="0 auto"></u-line>
  40 + <view class="detail-item" v-if="!commandDetail.request.oneway">
  41 + <view class="detail-label">响应结果</view>
  42 + <view class="detail-value">{{ commandDetail.response?JSON.stringify(commandDetail.response):'无' }}
  43 + </view>
  44 + </view>
  45 + </view>
  46 + <view class="command">命令内容</view>
  47 + <u-textarea :value="formatValue(commandDetail.request.body)" disabled></u-textarea>
  48 + <view style="height: 50rpx;"></view>
  49 + </view>
  50 +</template>
  51 +
  52 +<script>
  53 + import {
  54 + formatToDate
  55 + } from '@/plugins/utils.js';
  56 + export default {
  57 + data() {
  58 + return {
  59 + commandDetail: {},
  60 + failContent: ""
  61 + };
  62 + },
  63 + computed: {
  64 + deviceType() {
  65 + return this.commandDetail.deviceType === 'DIRECT_CONNECTION' ?
  66 + '直连设备' :
  67 + this.commandDetail.deviceType === 'GATEWAY' ?
  68 + '网关设备' :
  69 + this.commandDetail.deviceType === 'SENSOR' ?
  70 + '网关子设备' :
  71 + '';
  72 + }
  73 + },
  74 + methods: {
  75 + format(date) {
  76 + return formatToDate(date, 'YYYY-MM-DD HH:mm:ss');
  77 + },
  78 + formatValue(value) {
  79 + try {
  80 + const val = JSON.parse(value['params']);
  81 + //微信小程序端object无法显示,格式化为字符串
  82 + const stringifyVal = JSON.stringify(val['params'])
  83 + const formatVal = stringifyVal
  84 + .replace(/\\"/g, '"')
  85 + .replace(/]"/g, ']')
  86 + .replace(/"\[/g, '[');
  87 + return formatVal
  88 + } catch (e) {
  89 + return value['params']
  90 + }
  91 + }
  92 + },
  93 + onLoad(options) {
  94 + const { data } = options;
  95 + this.commandDetail = JSON.parse(decodeURIComponent(data));
  96 + if (this.commandDetail.response.status === 'SUCCESS') return
  97 + this.failContent = JSON.stringify(this.commandDetail.response.error)
  98 + }
  99 + };
  100 +</script>
  101 +
  102 +<style lang="scss" scoped>
  103 + @import "../static/command-detail.scss";
  104 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <view class="command-record">
  3 + <!-- 命令记录筛选-->
  4 + <view class="filter-button" @click="openSearchDialog">
  5 + <text>筛选</text>
  6 + <image src="/static/shaixuan.png" />
  7 + </view>
  8 + <!-- 命令记录分页 -->
  9 + <mescroll-uni ref="mescrollRef" @init="mescrollInit" :down="downOption" @down="downCallback" @up="upCallback"
  10 + height="700px">
  11 + <view @click="openCommandDetail(item)" class="list-item" v-for="(item, index) in list" :key="index">
  12 + <view class="item">
  13 + <view class="item-first">
  14 + <text>{{ item.deviceName }}</text>
  15 + <!-- 业务 单向是没有响应状态 -->
  16 + <view v-if="!item.request.oneway">
  17 + <view class="item-right item-success" v-if="item.response">响应成功</view>
  18 + <view class="item-right item-fail" v-else>响应失败</view>
  19 + </view>
  20 + </view>
  21 + <view>
  22 + 命令类型:
  23 + <text class="ml-16">{{ item.additionalInfo.cmdType===1?'服务':'自定义' }}</text>
  24 + </view>
  25 + <view v-if="item.statusName">
  26 + 命令状态:
  27 + <text :style="{color:formatCommandStatus(item.status)}" class="ml-16">
  28 + {{ item.statusName }}
  29 + </text>
  30 + </view>
  31 + <view class="item-first">
  32 + <view>
  33 + 响应类型:
  34 + <text class="ml-16">{{ !item.request.oneway?'双向':'单向' }}</text>
  35 + </view>
  36 + <view class="time">{{ format(item.createTime) }}</view>
  37 + </view>
  38 + </view>
  39 + </view>
  40 + </mescroll-uni>
  41 + <!-- 命令记录弹窗筛选 -->
  42 + <u-popup @close="close" closeable bgColor="#fff" :show="show" mode="bottom" :round="20">
  43 + <view class="filter" @touchmove.stop.prevent="disabledScroll">
  44 + <view class="filter-title"><text>筛选条件</text></view>
  45 + <query-item :filterList="issueStatus" title="下发状态"
  46 + @clickTag="currentIndex => handleClickTag(currentIndex, issueStatus)"></query-item>
  47 + <view class="flex-column">
  48 + <view class="mt-3 command-time-text">命令下发时间</view>
  49 + <view class="mt-3">
  50 + <uni-datetime-picker return-type="timestamp" v-model="range" type="datetimerange"
  51 + rangeSeparator="至" />
  52 + </view>
  53 + </view>
  54 + <view class="h-30"></view>
  55 + <view class="button-group">
  56 + <view>
  57 + <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="重置"
  58 + @click="resetFilter"></u-button>
  59 + </view>
  60 + <view>
  61 + <u-button color="#3388ff" shape="circle" text="确认" @click="confirmFilter"></u-button>
  62 + </view>
  63 + </view>
  64 + </view>
  65 + </u-popup>
  66 + </view>
  67 +</template>
  68 +<script>
  69 + import queryItem from '@/pages/device/components/query-item.vue';
  70 + import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
  71 + import {
  72 + formatToDate
  73 + } from '@/plugins/utils.js';
  74 + import {
  75 + debounce
  76 + } from '@/plugins/throttle.js';
  77 + import {
  78 + issueStatus
  79 + } from '../config/data.js'
  80 + import api from '@/api/index.js'
  81 + import {
  82 + useNavigateTo
  83 + } from '@/plugins/utils.js'
  84 +
  85 + export default {
  86 + mixins: [MescrollMixin],
  87 + components: {
  88 + queryItem
  89 + },
  90 + props: {
  91 + tbDeviceId: {
  92 + type: String,
  93 + default: ''
  94 + }
  95 + },
  96 + data() {
  97 + return {
  98 + show: false,
  99 + list: [],
  100 + total: 0,
  101 + range: [],
  102 + formTime: {
  103 + status: '',
  104 + startTime: '',
  105 + endTime: ''
  106 + },
  107 + status: '',
  108 + issueStatus,
  109 + downOption: {
  110 + auto: false //是否在初始化后,自动执行downCallback; 默认true
  111 + },
  112 + page: {
  113 + num: 0,
  114 + size: 10
  115 + }
  116 + };
  117 + },
  118 + methods: {
  119 + formatCommandStatus(status) {
  120 + return status == 'EXPIRED' ?
  121 + 'red' :
  122 + status == 'DELIVERED' ?
  123 + 'blue' :
  124 + status == 'QUEUED' ?
  125 + '#00C9A7' :
  126 + status == 'TIMEOUT' ?
  127 + 'red' :
  128 + status == 'SENT' ?
  129 + '#00C9A7' : ''
  130 + },
  131 + downCallback() {
  132 + for (let i in this.formTime) Reflect.set(this.formTime, i, '')
  133 + this.list = [];
  134 + this.page.num = 1;
  135 + this.loadData(this.page.num, {
  136 + tbDeviceId: this.tbDeviceId
  137 + });
  138 + },
  139 + format(date) {
  140 + return formatToDate(date, 'YYYY-MM-DD HH:mm:ss');
  141 + },
  142 + disabledScroll() {
  143 + return;
  144 + },
  145 + upCallback() {
  146 + const tbDeviceId = {
  147 + tbDeviceId: this.tbDeviceId
  148 + }
  149 + const condition = Object.values(this.formTime)
  150 + if (condition.length === 0) {
  151 + this.page.num += 1;
  152 + this.loadData(this.page.num, {
  153 + ...tbDeviceId
  154 + });
  155 + } else if (condition.filter(Boolean).length > 0) {
  156 + this.page.num += 1;
  157 + this.loadData(this.page.num, {
  158 + ...this.formTime,
  159 + ...tbDeviceId
  160 + });
  161 + } else {
  162 + this.page.num += 1;
  163 + this.loadData(this.page.num, {
  164 + ...tbDeviceId
  165 + });
  166 + }
  167 + },
  168 + async loadData(pageNo, params = {}) {
  169 + let httpData = {
  170 + ...params,
  171 + page: pageNo,
  172 + pageSize: 10
  173 + };
  174 + const res = await api.deviceApi.getRpcRecord({
  175 + params: httpData,
  176 + custom: {
  177 + load: false
  178 + }
  179 + })
  180 + if (!res) return
  181 + this.total = res.total;
  182 + uni.stopPullDownRefresh();
  183 + this.mescroll.endByPage(res.items.length, res.total);
  184 + if (pageNo == 1) {
  185 + this.list = res.items;
  186 + } else {
  187 + this.list = this.list.concat(res.items);
  188 + }
  189 + },
  190 + handleClickTag(currentIndex, list) {
  191 + list.map((item, index) => {
  192 + item.checked = index === currentIndex;
  193 + });
  194 + },
  195 + resetFilter() {
  196 + const {
  197 + issueStatus
  198 + } = this;
  199 + issueStatus.forEach(item => item.checked = false)
  200 + issueStatus[0].checked = true
  201 + },
  202 + close() {
  203 + this.show = false;
  204 + },
  205 + openSearchDialog() {
  206 + this.show = true;
  207 + this.resetFilter()
  208 + this.range = []
  209 + for (let i in this.formTime) Reflect.set(this.formTime, i, '')
  210 + },
  211 + hideKeyboard() {
  212 + uni.hideKeyboard();
  213 + },
  214 + confirmFilter() {
  215 + this.formTime.startTime = this.range[0]
  216 + this.formTime.endTime = this.range[1]
  217 + const issueStatus = this.issueStatus.find(item => item.checked);
  218 + this.formTime.status = issueStatus.type ? issueStatus.type : undefined,
  219 + this.loadData(1, {
  220 + tbDeviceId: this.tbDeviceId,
  221 + ...this.formTime
  222 + });
  223 + this.show = false;
  224 + },
  225 + openCommandDetail(item) {
  226 + useNavigateTo('/device-subpackage/device-detail/components/command-detail?data=', item)
  227 + }
  228 + }
  229 + };
  230 +</script>
  231 +
  232 +
  233 +<style lang="scss" scoped>
  234 + @import "../static/command-record.scss";
  235 +</style>
\ No newline at end of file
... ...
device-subpackage/device-detail/components/history-data.vue renamed from deviceSubPage/deviceDetailPage/tabDetail/historyData.vue
1   -<template>
2   - <view class="historyData">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <view class="historyData-top">
6   - <u-form :label-style="{ 'font-size': '0rpx' }">
7   - <u-form-item @click="openCalendar">
8   - <u-input v-model="timeData.selectTime" disabled disabledColor="#fff" placeholder="请选择日期"
9   - border="none" suffixIcon="arrow-down">
10   - <template slot="prefix">
11   - <image class="icon" src="../../../static/can-der.png"></image>
12   - </template>
13   - </u-input>
14   - </u-form-item>
15   - <u-form-item @click="openTimeGap">
16   - <u-input v-model="timeData.getTimeGap" disabled disabledColor="#fff" placeholder="请选择时间区间"
17   - border="none" suffixIcon="arrow-down">
18   - <template slot="prefix">
19   - <image class="icon" src="../../../static/time.png"></image>
20   - </template>
21   - </u-input>
22   - </u-form-item>
23   - <u-form-item @click="openAvg">
24   - <u-input shape="circle" v-model="aggText" placeholder="请选择数据聚合功能" disabled disabledColor="#377DFF0D"
25   - suffixIcon="arrow-down" />
26   - </u-form-item>
27   - <u-form-item @click="openTimeGap" v-if="limitFlag">
28   - <view class="u-flex">
29   - <text>最大条数</text>
30   - <u-number-box style="margin-left:30rpx" class="ml-10" v-model="timeData.limit" :min="7"
31   - :max="50000"></u-number-box>
32   - </view>
33   - </u-form-item>
34   - <u-form-item @click="openType">
35   - <u-input shape="circle" v-model="timeData.getType" placeholder="请选择属性" disabled
36   - disabledColor="#377DFF0D" suffixIcon="arrow-down" />
37   - </u-form-item>
38   - </u-form>
39   -
40   - <view class="charts-box" v-show="historyData.length">
41   - <qiun-data-charts type="area" canvas2d canvasId="daskujdhasljkdcnzjkdfhuoqwlqwjhkdsamjczxnmdasd123321"
42   - :chartData="chartData" :ontouch="true"
43   - :opts="{ xAxis: { disabled: true, itemCount: 6, scrollShow: true }, legend: { show: false }, enableScroll: true }" />
44   - </view>
45   - <mescroll-empty v-if="!historyData.length" />
46   - </view>
47   - <view class="historyData-bottom" v-show="historyData.length">
48   - <view class="table">
49   - <view class="tr bg-w" v-if="historyData.length">
50   - <view class="th">变量值</view>
51   - <view class="th">更新时间</view>
52   - </view>
53   - <view class="tr bg-g" :class="{ odd: index % 2 === 0 }" v-for="(item, index) in historyData"
54   - :key="index">
55   - <view class="td">{{ item.value }}</view>
56   - <view class="td">{{ item.ts }}</view>
57   - </view>
58   - </view>
59   - </view>
60   - <u-calendar :show="showCalendar" :defaultDate="defaultDate" closeOnClickOverlay mode="range" startText="开始时间"
61   - endText="结束时间" confirmDisabledText="请选择日期" :minDate="minDate" :maxDate="maxDate" @confirm="calendarConfirm"
62   - @close="calendarClose"></u-calendar>
63   - <u-picker :show="showTimeGap" :columns="columns" keyName="label" closeOnClickOverlay @confirm="confirmTimeGap"
64   - @cancel="cancelTimeGap" @close="cancelTimeGap" :defaultIndex="[3]"></u-picker>
65   - <u-picker :show="showSelectType" :columns="keys" closeOnClickOverlay @confirm="confirmTypeGap"
66   - @cancel="cancelTypeGap" @close="cancelTypeGap"></u-picker>
67   - <u-picker :show="showSelectAvg" :columns="avgColumns" keyName="label" closeOnClickOverlay
68   - @confirm="confirmAvgGap" @cancel="showSelectAvg=false" @close="showSelectAvg=false"></u-picker>
69   - </view>
70   -</template>
71   -
72   -<script>
73   - import fTabbar from '@/components/module/f-tabbar/f-tabbar';
74   - import qiunDataCharts from '@/uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue';
75   - import {
76   - getHistoryData
77   - } from '../api/index.js';
78   - import {
79   - formatToDate
80   - } from '@/plugins/utils.js';
81   - const d = new Date();
82   - const year = d.getFullYear();
83   - let month = d.getMonth() + 1;
84   - month = month < 10 ? `0${month}` : month;
85   - const date = d.getDate();
86   - export default {
87   - components: {
88   - fTabbar,
89   - qiunDataCharts
90   - },
91   - props: {
92   - keys: {
93   - type: Array,
94   - default: () => []
95   - },
96   - yesterday: {
97   - type: String,
98   - default: ''
99   - },
100   - today: {
101   - type: String,
102   - default: ''
103   - },
104   - timeDiff: {
105   - type: String,
106   - default: ''
107   - },
108   - historyData: {
109   - type: Array,
110   - default: () => []
111   - },
112   - entityId: {
113   - type: String,
114   - required: true
115   - },
116   - start: {
117   - type: String,
118   - required: true
119   - },
120   - end: {
121   - type: String,
122   - required: true
123   - }
124   - },
125   - data() {
126   - return {
127   - limitFlag: true,
128   - avgColumns: [
129   - [{
130   - label: '最小值',
131   - value: 'MIN'
132   - }, {
133   - label: '最大值',
134   - value: 'MAX'
135   - },
136   - {
137   - label: '平均值',
138   - value: 'AVG'
139   - },
140   - {
141   - label: '求和',
142   - value: 'SUM'
143   - },
144   - {
145   - label: '计数',
146   - value: 'COUNT'
147   - },
148   - {
149   - label: '空',
150   - value: 'NONE'
151   - },
152   - ]
153   - ],
154   - startTs: this.start,
155   - endTs: this.end,
156   - showCalendar: false,
157   - showTimeGap: false,
158   - showSelectType: false,
159   - showSelectAvg: false,
160   - minDate: `${year}-${month - 1}-${date}`,
161   - maxDate: `${year}-${month}-${date + 1}`,
162   - defaultDate: [this.yesterday, this.today],
163   - chartData: {
164   - categories: this.historyData.length && this.historyData.map(item => item.ts),
165   - series: [{
166   - name: this.keys[0][0],
167   - data: this.historyData.length && this.historyData.map(item => Number(item.value))
168   - }]
169   - },
170   - columns: [
171   - [{
172   - label: '5分钟',
173   - value: 300000
174   - },
175   - {
176   - label: '10分钟',
177   - value: 600000
178   - },
179   - {
180   - label: '15分钟',
181   - value: 900000
182   - },
183   - {
184   - label: '30分钟',
185   - value: 1800000
186   - },
187   - {
188   - label: '1小时',
189   - value: 3600000
190   - },
191   - {
192   - label: '2小时',
193   - value: 7200000
194   - }
195   - ]
196   - ],
197   - timeData: {
198   - selectTime: this.yesterday + ' 至 ' + this.today,
199   - getTimeGap: this.timeDiff,
200   - getType: this.keys[0][0],
201   - limit: 7,
202   - agg: 'NONE'
203   - },
204   - aggText: '空'
205   - };
206   - },
207   - watch: {
208   - historyData(newValue) {
209   - if (!newValue.length) {
210   - this.chartData.categories = [];
211   - this.chartData.series = [];
212   - } else {
213   - this.chartData.categories = newValue.map(item => item.ts);
214   - this.chartData.series = [{
215   - name: this.keys[0][0],
216   - data: newValue.map(item => Number(item.value))
217   - }];
218   - }
219   - }
220   - },
221   - methods: {
222   - // 动态生成Columns
223   - generateColumns(value) {
224   - if (value < 604800000) {
225   - // 小于7天
226   - return [
227   - [{
228   - label: '5分钟',
229   - value: 300000
230   - },
231   - {
232   - label: '10分钟',
233   - value: 600000
234   - },
235   - {
236   - label: '15分钟',
237   - value: 900000
238   - },
239   - {
240   - label: '30分钟',
241   - value: 1800000
242   - },
243   - {
244   - label: '1小时',
245   - value: 3600000
246   - },
247   - {
248   - label: '2小时',
249   - value: 7200000
250   - }
251   - ]
252   - ];
253   - } else if (value < 2592000000) {
254   - // 小于30天
255   - return [
256   - [{
257   - label: '30分钟',
258   - value: 1800000
259   - },
260   - {
261   - label: '1小时',
262   - value: 3600000
263   - },
264   - {
265   - label: '2小时',
266   - value: 7200000
267   - },
268   - {
269   - label: '5小时',
270   - value: 18000000
271   - },
272   - {
273   - label: '10小时',
274   - value: 36000000
275   - },
276   - {
277   - label: '12小时',
278   - value: 43200000
279   - },
280   - {
281   - label: '1天',
282   - value: 86400000
283   - }
284   - ]
285   - ];
286   - } else if (value >= 2592000000) {
287   - // 大于30天
288   - return [
289   - [{
290   - label: '2小时',
291   - value: 7200000
292   - },
293   - {
294   - label: '5小时',
295   - value: 18000000
296   - },
297   - {
298   - label: '10小时',
299   - value: 36000000
300   - },
301   - {
302   - label: '12小时',
303   - value: 43200000
304   - },
305   - {
306   - label: '1天',
307   - value: 86400000
308   - }
309   - ]
310   - ];
311   - }
312   - },
313   - openCalendar() {
314   - this.showCalendar = true;
315   - },
316   - openTimeGap() {
317   - this.showTimeGap = true;
318   - },
319   - openType() {
320   - this.showSelectType = true;
321   - },
322   - openAvg() {
323   - this.showSelectAvg = true
324   - },
325   - calendarConfirm(date) {
326   - this.showCalendar = false;
327   - this.timeData.selectTime = `${date[0]} 至 ${date[date.length - 1]}`;
328   - // 选择的日期时间差(时间戳)
329   - const timeDiff = formatToDate(date[date.length - 1], 'x') - formatToDate(date[0], 'x');
330   - const genColumns = this.generateColumns(timeDiff);
331   - this.columns = genColumns;
332   - this.timeData.getTimeGap = '';
333   - this.timeData.getType = '';
334   - this.startTs = formatToDate(date[0], 'x');
335   - // 最后时间的最后一秒
336   - this.endTs = formatToDate(`${date[date.length - 1]} 23:59:59`, 'x');
337   - },
338   - calendarClose() {
339   - this.showCalendar = false;
340   - },
341   - confirmTimeGap(time) {
342   - this.showTimeGap = false;
343   - this.timeData.getTimeGap = time.value[0].label;
344   - this.timeData.getType = '';
345   - },
346   -
347   - cancelTimeGap() {
348   - this.showTimeGap = false;
349   - },
350   - confirmAvgGap(e) {
351   - this.timeData.agg = e.value[0].value
352   - this.aggText = e.value[0].label
353   - if (e.value[0].value === 'NONE') {
354   - this.limitFlag = true
355   - this.timeData.limit = 7
356   - } else {
357   - this.timeData.limit = null
358   - this.limitFlag = false
359   - }
360   - this.showSelectAvg = false
361   - },
362   - async confirmTypeGap(time) {
363   - this.showSelectType = false;
364   - this.timeData.getType = time.value[0];
365   - const interval = this.columns[0].find(item => item.label === this.timeData.getTimeGap);
366   - const data = await getHistoryData({
367   - startTs: this.startTs,
368   - endTs: this.endTs,
369   - keys: this.timeData.getType,
370   - interval: this.limitFlag ? null : interval.value,
371   - entityId: this.entityId,
372   - limit: this.timeData.limit,
373   - agg: this.timeData.agg
374   - });
375   - this.$emit('update', data[this.timeData.getType]);
376   - },
377   - cancelTypeGap() {
378   - this.showSelectType = false;
379   - }
380   - }
381   - };
382   -</script>
383   -
384   -<style lang="scss" scoped>
385   - .charts-box {
386   - width: 100%;
387   - height: 550rpx;
388   - }
389   -
390   - .historyData {
391   - margin: 30rpx;
392   -
393   - .historyData-top {
394   - padding: 30rpx;
395   - background-color: #fff;
396   - // height: 870rpx;
397   - border-radius: 20rpx;
398   -
399   - .icon {
400   - width: 28rpx;
401   - height: 28rpx;
402   - margin-right: 15rpx;
403   - }
404   - }
405   -
406   - .historyData-bottom {
407   - margin-top: 30rpx;
408   - background-color: #fff;
409   - border-radius: 20rpx;
410   -
411   - .table {
412   - border: 0px solid darkgray;
413   -
414   - .tr {
415   - display: flex;
416   - width: 100%;
417   - justify-content: center;
418   - height: 3rem;
419   - align-items: center;
420   -
421   - .th {
422   - display: flex;
423   - justify-content: center;
424   - align-items: center;
425   - width: 50%;
426   - color: #333;
427   - font-weight: 500;
428   - }
429   -
430   - .td {
431   - color: #999;
432   - width: 50%;
433   - display: flex;
434   - justify-content: center;
435   - text-align: center;
436   - }
437   - }
438   - }
439   - }
440   - }
441   -
442   - .odd {
443   - background-color: #f9fcff;
444   - }
  1 +<template>
  2 + <view class="historyData">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <view class="historyData-top">
  6 + <u-form :label-style="{ 'font-size': '0rpx' }">
  7 + <u-form-item @click="openCalendar">
  8 + <u-input v-model="timeData.selectTime" disabled disabledColor="#fff" placeholder="请选择日期"
  9 + border="none" suffixIcon="arrow-down">
  10 + <template slot="prefix">
  11 + <image class="icon" src="../../../static/can-der.png"></image>
  12 + </template>
  13 + </u-input>
  14 + </u-form-item>
  15 + <u-form-item @click="openTimeGap">
  16 + <u-input v-model="timeData.getTimeGap" disabled disabledColor="#fff" placeholder="请选择时间区间"
  17 + border="none" suffixIcon="arrow-down">
  18 + <template slot="prefix">
  19 + <image class="icon" src="../../../static/time.png"></image>
  20 + </template>
  21 + </u-input>
  22 + </u-form-item>
  23 + <u-form-item @click="openAvg">
  24 + <u-input shape="circle" v-model="aggText" placeholder="请选择数据聚合功能" disabled disabledColor="#377DFF0D"
  25 + suffixIcon="arrow-down" />
  26 + </u-form-item>
  27 + <u-form-item @click="openTimeGap" v-if="limitFlag">
  28 + <view class="u-flex">
  29 + <text>最大条数</text>
  30 + <u-number-box style="margin-left:30rpx" class="ml-10" v-model="timeData.limit" :min="7"
  31 + :max="50000"></u-number-box>
  32 + </view>
  33 + </u-form-item>
  34 + <u-form-item @click="openType">
  35 + <u-input shape="circle" v-model="timeData.getType" placeholder="请选择属性" disabled
  36 + disabledColor="#377DFF0D" suffixIcon="arrow-down" />
  37 + </u-form-item>
  38 + </u-form>
  39 +
  40 + <view class="charts-box" v-show="historyData.length">
  41 + <qiun-data-charts type="area" canvas2d canvasId="daskujdhasljkdcnzjkdfhuoqwlqwjhkdsamjczxnmdasd123321"
  42 + :chartData="chartData" :ontouch="true"
  43 + :opts="{ xAxis: { disabled: true, itemCount: 6, scrollShow: true }, legend: { show: false }, enableScroll: true }" />
  44 + </view>
  45 + <mescroll-empty v-if="!historyData.length" />
  46 + </view>
  47 + <view class="historyData-bottom" v-show="historyData.length">
  48 + <view class="table">
  49 + <view class="tr bg-w" v-if="historyData.length">
  50 + <view class="th">变量值</view>
  51 + <view class="th">更新时间</view>
  52 + </view>
  53 + <view class="tr bg-g" :class="{ odd: index % 2 === 0 }" v-for="(item, index) in historyData"
  54 + :key="index">
  55 + <view class="td">{{ item.value }}</view>
  56 + <view class="td">{{ item.ts }}</view>
  57 + </view>
  58 + </view>
  59 + </view>
  60 + <u-calendar :show="showCalendar" :defaultDate="defaultDate" closeOnClickOverlay mode="range" startText="开始时间"
  61 + endText="结束时间" confirmDisabledText="请选择日期" :minDate="minDate" :maxDate="maxDate" @confirm="calendarConfirm"
  62 + @close="calendarClose"></u-calendar>
  63 + <u-picker :show="showTimeGap" :columns="columns" keyName="label" closeOnClickOverlay @confirm="confirmTimeGap"
  64 + @cancel="cancelTimeGap" @close="cancelTimeGap" :defaultIndex="[3]"></u-picker>
  65 + <u-picker :show="showSelectType" :columns="keys" closeOnClickOverlay @confirm="confirmTypeGap"
  66 + @cancel="cancelTypeGap" @close="cancelTypeGap"></u-picker>
  67 + <u-picker :show="showSelectAvg" :columns="avgColumns" keyName="label" closeOnClickOverlay
  68 + @confirm="confirmAvgGap" @cancel="showSelectAvg=false" @close="showSelectAvg=false"></u-picker>
  69 + </view>
  70 +</template>
  71 +
  72 +<script>
  73 + import fTabbar from '@/components/module/f-tabbar/f-tabbar';
  74 + import qiunDataCharts from './uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue';
  75 + import {
  76 + getHistoryData
  77 + } from '../api/index.js';
  78 + import {
  79 + formatToDate
  80 + } from '@/plugins/utils.js';
  81 + const d = new Date();
  82 + const year = d.getFullYear();
  83 + let month = d.getMonth() + 1;
  84 + month = month < 10 ? `0${month}` : month;
  85 + const date = d.getDate();
  86 + export default {
  87 + components: {
  88 + fTabbar,
  89 + qiunDataCharts
  90 + },
  91 + props: {
  92 + keys: {
  93 + type: Array,
  94 + default: () => []
  95 + },
  96 + yesterday: {
  97 + type: String,
  98 + default: ''
  99 + },
  100 + today: {
  101 + type: String,
  102 + default: ''
  103 + },
  104 + timeDiff: {
  105 + type: String,
  106 + default: ''
  107 + },
  108 + historyData: {
  109 + type: Array,
  110 + default: () => []
  111 + },
  112 + entityId: {
  113 + type: String,
  114 + required: true
  115 + },
  116 + start: {
  117 + type: String,
  118 + required: true
  119 + },
  120 + end: {
  121 + type: String,
  122 + required: true
  123 + }
  124 + },
  125 + data() {
  126 + return {
  127 + limitFlag: true,
  128 + avgColumns: [
  129 + [{
  130 + label: '最小值',
  131 + value: 'MIN'
  132 + }, {
  133 + label: '最大值',
  134 + value: 'MAX'
  135 + },
  136 + {
  137 + label: '平均值',
  138 + value: 'AVG'
  139 + },
  140 + {
  141 + label: '求和',
  142 + value: 'SUM'
  143 + },
  144 + {
  145 + label: '计数',
  146 + value: 'COUNT'
  147 + },
  148 + {
  149 + label: '空',
  150 + value: 'NONE'
  151 + },
  152 + ]
  153 + ],
  154 + startTs: this.start,
  155 + endTs: this.end,
  156 + showCalendar: false,
  157 + showTimeGap: false,
  158 + showSelectType: false,
  159 + showSelectAvg: false,
  160 + minDate: `${year}-${month - 1}-${date}`,
  161 + maxDate: `${year}-${month}-${date + 1}`,
  162 + defaultDate: [this.yesterday, this.today],
  163 + chartData: {
  164 + categories: this.historyData.length && this.historyData.map(item => item.ts),
  165 + series: [{
  166 + name: this.keys[0][0],
  167 + data: this.historyData.length && this.historyData.map(item => Number(item.value))
  168 + }]
  169 + },
  170 + columns: [
  171 + [{
  172 + label: '5分钟',
  173 + value: 300000
  174 + },
  175 + {
  176 + label: '10分钟',
  177 + value: 600000
  178 + },
  179 + {
  180 + label: '15分钟',
  181 + value: 900000
  182 + },
  183 + {
  184 + label: '30分钟',
  185 + value: 1800000
  186 + },
  187 + {
  188 + label: '1小时',
  189 + value: 3600000
  190 + },
  191 + {
  192 + label: '2小时',
  193 + value: 7200000
  194 + }
  195 + ]
  196 + ],
  197 + timeData: {
  198 + selectTime: this.yesterday + ' 至 ' + this.today,
  199 + getTimeGap: this.timeDiff,
  200 + getType: this.keys[0][0],
  201 + limit: 7,
  202 + agg: 'NONE'
  203 + },
  204 + aggText: '空'
  205 + };
  206 + },
  207 + watch: {
  208 + historyData(newValue) {
  209 + if (!newValue.length) {
  210 + this.chartData.categories = [];
  211 + this.chartData.series = [];
  212 + } else {
  213 + this.chartData.categories = newValue.map(item => item.ts);
  214 + this.chartData.series = [{
  215 + name: this.keys[0][0],
  216 + data: newValue.map(item => Number(item.value))
  217 + }];
  218 + }
  219 + }
  220 + },
  221 + methods: {
  222 + // 动态生成Columns
  223 + generateColumns(value) {
  224 + if (value < 604800000) {
  225 + // 小于7天
  226 + return [
  227 + [{
  228 + label: '5分钟',
  229 + value: 300000
  230 + },
  231 + {
  232 + label: '10分钟',
  233 + value: 600000
  234 + },
  235 + {
  236 + label: '15分钟',
  237 + value: 900000
  238 + },
  239 + {
  240 + label: '30分钟',
  241 + value: 1800000
  242 + },
  243 + {
  244 + label: '1小时',
  245 + value: 3600000
  246 + },
  247 + {
  248 + label: '2小时',
  249 + value: 7200000
  250 + }
  251 + ]
  252 + ];
  253 + } else if (value < 2592000000) {
  254 + // 小于30天
  255 + return [
  256 + [{
  257 + label: '30分钟',
  258 + value: 1800000
  259 + },
  260 + {
  261 + label: '1小时',
  262 + value: 3600000
  263 + },
  264 + {
  265 + label: '2小时',
  266 + value: 7200000
  267 + },
  268 + {
  269 + label: '5小时',
  270 + value: 18000000
  271 + },
  272 + {
  273 + label: '10小时',
  274 + value: 36000000
  275 + },
  276 + {
  277 + label: '12小时',
  278 + value: 43200000
  279 + },
  280 + {
  281 + label: '1天',
  282 + value: 86400000
  283 + }
  284 + ]
  285 + ];
  286 + } else if (value >= 2592000000) {
  287 + // 大于30天
  288 + return [
  289 + [{
  290 + label: '2小时',
  291 + value: 7200000
  292 + },
  293 + {
  294 + label: '5小时',
  295 + value: 18000000
  296 + },
  297 + {
  298 + label: '10小时',
  299 + value: 36000000
  300 + },
  301 + {
  302 + label: '12小时',
  303 + value: 43200000
  304 + },
  305 + {
  306 + label: '1天',
  307 + value: 86400000
  308 + }
  309 + ]
  310 + ];
  311 + }
  312 + },
  313 + openCalendar() {
  314 + this.showCalendar = true;
  315 + },
  316 + openTimeGap() {
  317 + this.showTimeGap = true;
  318 + },
  319 + openType() {
  320 + this.showSelectType = true;
  321 + },
  322 + openAvg() {
  323 + this.showSelectAvg = true
  324 + },
  325 + calendarConfirm(date) {
  326 + this.showCalendar = false;
  327 + this.timeData.selectTime = `${date[0]} 至 ${date[date.length - 1]}`;
  328 + // 选择的日期时间差(时间戳)
  329 + const timeDiff = formatToDate(date[date.length - 1], 'x') - formatToDate(date[0], 'x');
  330 + const genColumns = this.generateColumns(timeDiff);
  331 + this.columns = genColumns;
  332 + this.timeData.getTimeGap = '';
  333 + this.timeData.getType = '';
  334 + this.startTs = formatToDate(date[0], 'x');
  335 + // 最后时间的最后一秒
  336 + this.endTs = formatToDate(`${date[date.length - 1]} 23:59:59`, 'x');
  337 + },
  338 + calendarClose() {
  339 + this.showCalendar = false;
  340 + },
  341 + confirmTimeGap(time) {
  342 + this.showTimeGap = false;
  343 + this.timeData.getTimeGap = time.value[0].label;
  344 + this.timeData.getType = '';
  345 + },
  346 +
  347 + cancelTimeGap() {
  348 + this.showTimeGap = false;
  349 + },
  350 + confirmAvgGap(e) {
  351 + this.timeData.agg = e.value[0].value
  352 + this.aggText = e.value[0].label
  353 + if (e.value[0].value === 'NONE') {
  354 + this.limitFlag = true
  355 + this.timeData.limit = 7
  356 + } else {
  357 + this.timeData.limit = null
  358 + this.limitFlag = false
  359 + }
  360 + this.showSelectAvg = false
  361 + },
  362 + async confirmTypeGap(time) {
  363 + this.showSelectType = false;
  364 + this.timeData.getType = time.value[0];
  365 + const interval = this.columns[0].find(item => item.label === this.timeData.getTimeGap);
  366 + const data = await getHistoryData({
  367 + startTs: this.startTs,
  368 + endTs: this.endTs,
  369 + keys: this.timeData.getType,
  370 + interval: this.limitFlag ? null : interval.value,
  371 + entityId: this.entityId,
  372 + limit: this.timeData.limit,
  373 + agg: this.timeData.agg
  374 + });
  375 + this.$emit('update', data[this.timeData.getType]);
  376 + },
  377 + cancelTypeGap() {
  378 + this.showSelectType = false;
  379 + }
  380 + }
  381 + };
  382 +</script>
  383 +
  384 +<style lang="scss" scoped>
  385 + .charts-box {
  386 + width: 100%;
  387 + height: 550rpx;
  388 + }
  389 +
  390 + .historyData {
  391 + margin: 30rpx;
  392 +
  393 + .historyData-top {
  394 + padding: 30rpx;
  395 + background-color: #fff;
  396 + // height: 870rpx;
  397 + border-radius: 20rpx;
  398 +
  399 + .icon {
  400 + width: 28rpx;
  401 + height: 28rpx;
  402 + margin-right: 15rpx;
  403 + }
  404 + }
  405 +
  406 + .historyData-bottom {
  407 + margin-top: 30rpx;
  408 + background-color: #fff;
  409 + border-radius: 20rpx;
  410 +
  411 + .table {
  412 + border: 0px solid darkgray;
  413 +
  414 + .tr {
  415 + display: flex;
  416 + width: 100%;
  417 + justify-content: center;
  418 + height: 3rem;
  419 + align-items: center;
  420 +
  421 + .th {
  422 + display: flex;
  423 + justify-content: center;
  424 + align-items: center;
  425 + width: 50%;
  426 + color: #333;
  427 + font-weight: 500;
  428 + }
  429 +
  430 + .td {
  431 + color: #999;
  432 + width: 50%;
  433 + display: flex;
  434 + justify-content: center;
  435 + text-align: center;
  436 + }
  437 + }
  438 + }
  439 + }
  440 + }
  441 +
  442 + .odd {
  443 + background-color: #f9fcff;
  444 + }
445 445 </style>
... ...
  1 +<template>
  2 + <view class="mp-u-modal">
  3 + <u-modal :mask-close-able="true" :show="showModal" closeOnClickOverlay :showConfirmButton="false"
  4 + @close="$emit('hideModal')" @touchmove.stop.prevent="disabledScroll" z-index="99999">
  5 + <view class="w-100 modal-content">
  6 + <view class="header-title">命令下发</view>
  7 + <view class="u-flex">
  8 + <text class="type-text">下发类型:</text>
  9 + <u-radio-group v-model="commandType" placement="row">
  10 + <u-radio activeColor="#3388FF" label="单向" name="OneWay"></u-radio>
  11 + <view style="margin: 0 20rpx;"></view>
  12 + <u-radio activeColor="#3388FF" label="双向" name="TwoWay"></u-radio>
  13 + </u-radio-group>
  14 + </view>
  15 + <view class="content-body">
  16 + <div class="u-flex u-row-between">
  17 + <u--textarea :placeholder="`请输入下发内容${isShowTCP?'(字符串格式)':'(json格式)'}`"
  18 + v-model="inputCommandVal" />
  19 + <u-icon v-if="!isShowTCP" @click="handleCopy(copyTextValue)" name="question-circle"
  20 + color="#2979ff" size="28" class="ml-10">
  21 + </u-icon>
  22 + </div>
  23 + </view>
  24 + <view class="button-group">
  25 + <view>
  26 + <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="取消"
  27 + @click="cancelCommand"></u-button>
  28 + </view>
  29 + <view>
  30 + <u-button color="#3388ff" shape="circle" text="确认" @click="confirmCommand"></u-button>
  31 + </view>
  32 + </view>
  33 + </view>
  34 + </u-modal>
  35 + </view>
  36 +</template>
  37 +
  38 +<script>
  39 + import {
  40 + useShowModal
  41 + } from '@/plugins/utils.js'
  42 +
  43 + export default {
  44 + props: {
  45 + showModal: Boolean,
  46 + isShowTCP: Boolean
  47 + },
  48 + data() {
  49 + return {
  50 + current: 0,
  51 + commandType: 'OneWay',
  52 + inputCommandVal: '',
  53 + copyTextValue: {
  54 + "method": "methodThingskit",
  55 + "params": {
  56 + "pin": 7,
  57 + "value": 1
  58 + }
  59 + }
  60 + }
  61 + },
  62 + methods: {
  63 + cancelCommand() {
  64 + this.$emit('cancelCommand')
  65 + },
  66 + confirmCommand() {
  67 + this.$emit('confirmCommand', this.commandType, this.inputCommandVal)
  68 + },
  69 + handleCopy(value) {
  70 + useShowModal(JSON.stringify(value), '命令下发', '复制内容').then(res => {
  71 + uni.setClipboardData({
  72 + data: JSON.stringify(value),
  73 + success: () => {
  74 + uni.showToast({
  75 + title: '复制成功'
  76 + })
  77 + }
  78 + });
  79 + })
  80 + },
  81 + reset() {
  82 + this.commandType = 'OneWay'
  83 + this.inputCommandVal = ''
  84 + }
  85 + }
  86 + }
  87 +</script>
  88 +
  89 +<style lang="scss" scoped>
  90 + .modal-content {
  91 + width: 720rpx;
  92 + padding: 0 30rpx;
  93 + background-color: white;
  94 +
  95 + .header-title {
  96 + text-align: center;
  97 + font-weight: 700;
  98 + margin-bottom: 40rpx;
  99 + }
  100 +
  101 + .type-text {
  102 + color: #333;
  103 + font-size: 14px;
  104 + font-weight: 700;
  105 + margin-right: 30rpx;
  106 + }
  107 +
  108 + .content-body {
  109 + margin-top: 28rpx;
  110 + width: 100%;
  111 + }
  112 +
  113 + .button-group {
  114 + display: flex;
  115 + margin-top: 40rpx;
  116 + justify-content: space-between;
  117 +
  118 + view {
  119 + width: 300rpx;
  120 + }
  121 + }
  122 + }
  123 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <view class="realtime-page">
  3 + <view class="item" v-for="(item, index) in recordList" :key="index">
  4 + <view class="item-top">
  5 + <view>{{ item.key }}</view>
  6 + <view class="item-value">{{ item.value }}</view>
  7 + </view>
  8 + <view class="item-time">{{ item.time }}</view>
  9 + </view>
  10 + <mescroll-empty v-if="!recordList.length" />
  11 + </view>
  12 +</template>
  13 +
  14 +<script>
  15 + export default {
  16 + props: {
  17 + recordList: {
  18 + type: Array,
  19 + default: () => []
  20 + }
  21 + }
  22 + };
  23 +</script>
  24 +
  25 +<style lang="scss" scoped>
  26 + @import "../static/realtime-data.scss";
  27 +</style>
\ No newline at end of file
... ...
  1 +const list = [{
  2 + name: "基础信息",
  3 + },
  4 + {
  5 + name: "实时数据",
  6 + },
  7 + {
  8 + name: "历史数据",
  9 + },
  10 + {
  11 + name: "告警记录",
  12 + },
  13 +]
  14 +
  15 +const issueStatus = [{
  16 + checked: true,
  17 + name: '全部',
  18 + type: ''
  19 + },
  20 + {
  21 + checked: false,
  22 + name: '队列中',
  23 + type: 'QUEUED'
  24 + },
  25 + {
  26 + checked: false,
  27 + name: '已发送',
  28 + type: 'SENT'
  29 + },
  30 + {
  31 + checked: false,
  32 + name: '发送成功',
  33 + type: 'DELIVERED'
  34 + },
  35 + {
  36 + checked: false,
  37 + name: '响应成功',
  38 + type: 'SUCCESSFUL'
  39 + },
  40 + {
  41 + checked: false,
  42 + name: '超时',
  43 + type: 'TIMEOUT'
  44 + },
  45 + {
  46 + checked: false,
  47 + name: '已过期',
  48 + type: 'EXPIRED'
  49 + },
  50 + {
  51 + checked: false,
  52 + name: '响应失败',
  53 + type: 'FAILED'
  54 + },
  55 + {
  56 + checked: false,
  57 + name: '已删除',
  58 + type: 'DELETED'
  59 + }
  60 +]
  61 +
  62 +const commandTypeList = [{
  63 + value: 'OneWay',
  64 + name: '单向',
  65 + },
  66 + {
  67 + value: 'TwoWay',
  68 + name: '双向'
  69 + },
  70 +]
  71 +
  72 +
  73 +export {
  74 + list,
  75 + issueStatus,
  76 + commandTypeList
  77 +}
\ No newline at end of file
... ...
device-subpackage/device-detail/device-detail.vue renamed from deviceSubPage/deviceDetailPage/deviceDetail.vue
1   -<template>
2   - <view class="device-detail-page">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <u-sticky bgColor="#fff">
6   - <u-tabs :list="list" :current="currentTab" @click="handleTabClick" :activeStyle="{
7   - fontWeight: 'bold',
8   - color: '#333',
9   - }" :inactiveStyle="{
10   - color: '#999',
11   - }" :scrollable="isScrollable" />
12   - </u-sticky>
13   - <view style="margin-top: 30rpx">
14   - <basicInfo v-show="currentTab == 0" :deviceDetail="deviceDetail" />
15   - <realTimeData v-show="currentTab === 1" :recordList="recordList" />
16   - <historyData v-if="currentTab === 2" :keys="keys" :yesterday="yesterday" :today="today" :timeDiff="timeDiff"
17   - :historyData="historyData" :entityId="entityId" :start="startTs" :end="endTs" @update="handleUpdate" />
18   - <alarmHistory v-show="currentTab === 3" :deviceId="deviceId" />
19   - <commondRecord v-if="currentTab === 4" :tbDeviceId="entityId" />
20   - </view>
21   - </view>
22   -</template>
23   -
24   -<script>
25   - import fTabbar from "@/components/module/f-tabbar/f-tabbar";
26   - import basicInfo from "./tabDetail/basicInfo.vue";
27   - import realTimeData from "./tabDetail/realtimeData.vue";
28   - import alarmHistory from "./tabDetail/alarmHistory.vue";
29   - import historyData from "./tabDetail/historyData.vue";
30   - import commondRecord from "./tabDetail/CommandRecord.vue";
31   - import {
32   - getDeviceKeys,
33   - getHistoryData
34   - } from "./api/index.js";
35   - import {
36   - formatToDate
37   - } from "@/plugins/utils.js";
38   - import MescrollCompMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mixins/mescroll-comp.js";
39   - import moment from "moment";
40   - import base from "@/config/baseUrl.js";
41   -
42   - export default {
43   - mixins: [MescrollCompMixin],
44   - components: {
45   - fTabbar,
46   - basicInfo,
47   - realTimeData,
48   - alarmHistory,
49   - historyData,
50   - commondRecord,
51   - },
52   - data() {
53   - return {
54   - list: [{
55   - name: "基础信息",
56   - },
57   - {
58   - name: "实时数据",
59   - },
60   - {
61   - name: "历史数据",
62   - },
63   - {
64   - name: "告警记录",
65   - },
66   - ],
67   - currentTab: 0,
68   - deviceId: "",
69   - deviceDetail: {},
70   - keys: [],
71   - yesterday: "",
72   - today: "",
73   - timeDiff: "",
74   - historyData: [],
75   - entityId: "",
76   - startTs: "",
77   - endTs: "",
78   - recordList: [],
79   - isScrollable: false,
80   - attrList: []
81   - };
82   - },
83   - onUnload() {
84   - // 页面关闭时,销毁webSocket连接,否则第二次会存在连接不到的情况
85   - uni.closeSocket();
86   - },
87   - async onLoad(options) {
88   - const {
89   - id,
90   - alarmStatus,
91   - lastOnlineTime,
92   - tbDeviceId,
93   - deviceProfileId
94   - } =
95   - options;
96   - this.deviceId = id;
97   - const res = await uni.$u.http.get(`/yt/device/${id}`);
98   - this.deviceDetail = {
99   - ...res,
100   - alarmStatus,
101   - lastOnlineTime,
102   - };
103   -
104   - // 设备类型不是网关子设备的添加一个命令记录的选项卡
105   - if (this.deviceDetail.deviceType !== "SENSOR") {
106   - this.list.push({
107   - name: "命令记录",
108   - });
109   - }
110   - this.isScrollable = this.list.length > 4;
111   - if (res.deviceProfileId) {
112   - const getAttrList = await uni.$u.http.get(
113   - `/yt/device/attributes/${res.deviceProfileId}`
114   - );
115   - if (Array.isArray(getAttrList)) {
116   - this.attrList = getAttrList.map(m => {
117   - return m.identifier
118   - })
119   - }
120   - }
121   - // 连接webSockte
122   - const socketTask = uni.connectSocket({
123   - url: `${base.socketPrefix}://${base.baseWebSocketUrl}/api/ws/plugins/telemetry?token=` +
124   - uni.getStorageSync("userInfo").isToken, //仅为示例,并非真实接口地址。
125   - complete: () => {},
126   - });
127   - uni.onSocketOpen((header) => {
128   - socketTask.send({
129   - data: JSON.stringify({
130   - attrSubCmds: [],
131   - tsSubCmds: [{
132   - entityType: "DEVICE",
133   - entityId: tbDeviceId,
134   - scope: "LATEST_TELEMETRY",
135   - cmdId: 1,
136   - keys: this.attrList.join(','),
137   - }, ],
138   - historyCmds: [],
139   - entityDataCmds: [],
140   - entityDataUnsubscribeCmds: [],
141   - alarmDataCmds: [],
142   - alarmDataUnsubscribeCmds: [],
143   - entityCountCmds: [],
144   - entityCountUnsubscribeCmds: [],
145   - }),
146   - success() {},
147   - });
148   - });
149   - socketTask.onMessage((msg) => {
150   - const {
151   - data
152   - } = JSON.parse(msg.data);
153   - const newArray = [];
154   - for (const key in data) {
155   - const [time, value] = data[key].flat(1);
156   - let obj = {
157   - key,
158   - time,
159   - value,
160   - };
161   - if (this.recordList.length === 0) {
162   - this.recordList.unshift(obj);
163   - } else {
164   - newArray.push(obj);
165   - }
166   - }
167   - newArray.forEach((item) => {
168   - let flag = false;
169   - this.recordList.forEach((item1) => {
170   - if (item1.key === item.key) {
171   - item1.value = item.value;
172   - item1.time = item.time;
173   - flag = true;
174   - }
175   - });
176   - if (!flag) {
177   - this.recordList.unshift(item);
178   - }
179   - });
180   - this.recordList = this.recordList.map((item) => {
181   - return {
182   - ...item,
183   - time: formatToDate(item.time, "YYYY-MM-DD HH:mm:ss"),
184   - };
185   - });
186   - });
187   -
188   - const keys = await getDeviceKeys(tbDeviceId);
189   - this.keys = [keys];
190   - // 昨天
191   - this.yesterday = moment().subtract(1, "days").format("YYYY-MM-DD");
192   - // 今天
193   - this.today = moment().format("YYYY-MM-DD");
194   - // 开始时间
195   - this.startTs = moment().subtract(1, "days").format("x");
196   - // 结束时间
197   - this.endTs = moment().format("x");
198   - this.entityId = tbDeviceId;
199   -
200   - const data = await getHistoryData({
201   - entityId: tbDeviceId,
202   - startTs: this.startTs,
203   - endTs: this.endTs,
204   - keys: keys[0],
205   - // interval: 1800000,
206   - limit: 7,
207   - agg: 'NONE'
208   - });
209   - this.timeDiff = "30分钟";
210   - if (!Object.keys(data).length) return;
211   -
212   - this.historyData = data[keys[0]].map((item) => {
213   - return {
214   - value: item.value,
215   - ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
216   - };
217   - });
218   - },
219   - methods: {
220   - handleTabClick({
221   - index
222   - }) {
223   - this.currentTab = index;
224   - },
225   - handleUpdate(data, e) {
226   - if (!Array.isArray(data)) {
227   - this.historyData = [];
228   - return;
229   - }
230   - this.historyData = data.map((item) => {
231   - return {
232   - value: item.value,
233   - ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
234   - };
235   - });
236   - },
237   - },
238   - };
239   -</script>
240   -
241   -<style lang="scss" scoped>
242   - .device-detail-page {
243   - height: 100vh;
244   - background-color: #f8f9fa;
245   - }
246   -</style>
  1 +<template>
  2 + <view class="device-detail-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <u-sticky :bgColor="bgColor">
  6 + <u-tabs :list="list" :current="currentTab" @click="handleTabClick" :activeStyle="activeColor"
  7 + :inactiveStyle="inActiveColor" :scrollable="isScrollable" />
  8 + </u-sticky>
  9 + <view class="mt-3">
  10 + <basic-info v-show="currentTab == 0" :deviceDetail="deviceDetail" />
  11 + <realtime-data v-show="currentTab === 1" :recordList="recordList" />
  12 + <history-data v-if="currentTab === 2" :keys="keys" :yesterday="yesterday" :today="today"
  13 + :timeDiff="timeDiff" :historyData="historyData" :entityId="entityId" :start="startTs" :end="endTs"
  14 + @update="handleUpdate" />
  15 + <alarm-history v-show="currentTab === 3" :deviceId="deviceId" />
  16 + <commond-record v-if="currentTab === 4" :tbDeviceId="entityId" />
  17 + </view>
  18 + </view>
  19 +</template>
  20 +
  21 +<script>
  22 + import fTabbar from "@/components/module/f-tabbar/f-tabbar";
  23 + import basicInfo from "./components/basic-info.vue";
  24 + import realtimeData from "./components/realtime-data.vue";
  25 + import alarmHistory from "./components/alarm-history.vue";
  26 + import historyData from "./components/history-data.vue";
  27 + import commondRecord from "./components/command-record.vue";
  28 + import { getDeviceKeys,getHistoryData } from "./api/index.js";
  29 + import {formatToDate} from "@/plugins/utils.js";
  30 + import MescrollCompMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mixins/mescroll-comp.js";
  31 + import moment from "moment";
  32 + import base from "@/config/baseUrl.js";
  33 + import { list } from './config/data.js'
  34 + import api from '@/api'
  35 +
  36 + export default {
  37 + mixins: [MescrollCompMixin],
  38 + components: {
  39 + fTabbar,
  40 + basicInfo,
  41 + realtimeData,
  42 + alarmHistory,
  43 + historyData,
  44 + commondRecord,
  45 + },
  46 + data() {
  47 + return {
  48 + bgColor: '#fff',
  49 + activeColor: {
  50 + fontWeight: 'bold',
  51 + color: '#333',
  52 + },
  53 + inActiveColor: {
  54 + color: '#999',
  55 + },
  56 + list,
  57 + currentTab: 0,
  58 + deviceId: "",
  59 + deviceDetail: {},
  60 + keys: [],
  61 + yesterday: "",
  62 + today: "",
  63 + timeDiff: "",
  64 + historyData: [],
  65 + entityId: "",
  66 + startTs: "",
  67 + endTs: "",
  68 + recordList: [], //实时数据
  69 + isScrollable: false,
  70 + attrList: []
  71 + };
  72 + },
  73 + onUnload() {
  74 + // 页面关闭时,销毁webSocket连接,否则第二次会存在连接不到的情况
  75 + uni.closeSocket();
  76 + },
  77 + async onLoad(options) {
  78 + const {id,alarmStatus,lastOnlineTime,tbDeviceId,deviceProfileId} = options;
  79 + this.deviceId = id;
  80 + const res = await api.deviceApi.getDeviceDetail(this.deviceId)
  81 + if(!res) return
  82 + this.deviceDetail = {
  83 + ...res,
  84 + alarmStatus,
  85 + lastOnlineTime,
  86 + };
  87 + // 设备类型不是网关子设备的添加一个命令记录的选项卡
  88 + if (this.deviceDetail.deviceType !== "SENSOR") {
  89 + this.list.push({
  90 + name: "命令记录",
  91 + });
  92 + const res = new Map()
  93 + this.list = this.list.filter((item) => !res.has(item.name) && res.set(item.name, 1))
  94 + } else {
  95 + this.list = this.list.filter(item => item.name !=='命令记录')
  96 + }
  97 + this.isScrollable = this.list.length > 4;
  98 + if (res.deviceProfileId) {
  99 + const getAttrList = await api.deviceApi.getAttribute(res.deviceProfileId)
  100 + if (Array.isArray(getAttrList)) {
  101 + this.attrList = getAttrList.map(m => {
  102 + return m.identifier
  103 + })
  104 + }
  105 + }
  106 + // 连接webSockte
  107 + const socketTask = uni.connectSocket({
  108 + url: `${base.socketPrefix}://${base.baseWebSocketUrl}/api/ws/plugins/telemetry?token=` + uni.getStorageSync("userInfo").isToken, //仅为示例,并非真实接口地址。
  109 + complete: () => {},
  110 + });
  111 + uni.onSocketOpen((header) => {
  112 + socketTask.send({
  113 + data: JSON.stringify({
  114 + attrSubCmds: [],
  115 + tsSubCmds: [{
  116 + entityType: "DEVICE",
  117 + entityId: tbDeviceId,
  118 + scope: "LATEST_TELEMETRY",
  119 + cmdId: 1,
  120 + keys: this.attrList.join(','),
  121 + }, ],
  122 + historyCmds: [],
  123 + entityDataCmds: [],
  124 + entityDataUnsubscribeCmds: [],
  125 + alarmDataCmds: [],
  126 + alarmDataUnsubscribeCmds: [],
  127 + entityCountCmds: [],
  128 + entityCountUnsubscribeCmds: [],
  129 + }),
  130 + success() {},
  131 + });
  132 + });
  133 + socketTask.onMessage((msg) => {
  134 + const { data } = JSON.parse(msg.data);
  135 + const newArray = [];
  136 + for (const key in data) {
  137 + const [time, value] = data[key].flat(1);
  138 + let obj = { key,time,value, };
  139 + if (this.recordList.length === 0) {
  140 + this.recordList.unshift(obj);
  141 + } else {
  142 + newArray.push(obj);
  143 + }
  144 + }
  145 + newArray.forEach((item) => {
  146 + let flag = false;
  147 + this.recordList.forEach((item1) => {
  148 + if (item1.key === item.key) {
  149 + item1.value = item.value;
  150 + item1.time = item.time;
  151 + flag = true;
  152 + }
  153 + });
  154 + if (!flag) {
  155 + this.recordList.unshift(item);
  156 + }
  157 + });
  158 + this.recordList = this.recordList.map((item) => {
  159 + return {
  160 + ...item,
  161 + time: formatToDate(item.time, "YYYY-MM-DD HH:mm:ss"),
  162 + };
  163 + });
  164 + });
  165 + const keys = await getDeviceKeys(tbDeviceId);
  166 + this.keys = [keys];
  167 + // 昨天
  168 + this.yesterday = moment().subtract(1, "days").format("YYYY-MM-DD");
  169 + // 今天
  170 + this.today = moment().format("YYYY-MM-DD");
  171 + // 开始时间
  172 + this.startTs = moment().subtract(1, "days").format("x");
  173 + // 结束时间
  174 + this.endTs = moment().format("x");
  175 + this.entityId = tbDeviceId;
  176 + const data = await getHistoryData({
  177 + entityId: tbDeviceId,
  178 + startTs: this.startTs,
  179 + endTs: this.endTs,
  180 + keys: keys[0],
  181 + // interval: 1800000,
  182 + limit: 7,
  183 + agg: 'NONE'
  184 + });
  185 + this.timeDiff = "30分钟";
  186 + if (!Object.keys(data).length) return;
  187 + this.historyData = data[keys[0]].map((item) => {
  188 + return {
  189 + value: item.value,
  190 + ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
  191 + };
  192 + });
  193 + },
  194 + methods: {
  195 + handleTabClick({
  196 + index
  197 + }) {
  198 + this.currentTab = index;
  199 + },
  200 + handleUpdate(data, e) {
  201 + if (!Array.isArray(data)) {
  202 + this.historyData = [];
  203 + return;
  204 + }
  205 + this.historyData = data.map((item) => {
  206 + return {
  207 + value: item.value,
  208 + ts: formatToDate(item.ts, "YYYY-MM-DD HH:mm:ss"),
  209 + };
  210 + });
  211 + },
  212 + },
  213 + };
  214 +</script>
  215 +
  216 +<style lang="scss" scoped>
  217 + .device-detail-page {
  218 + height: 100vh;
  219 + background-color: #f8f9fa;
  220 + }
  221 +</style>
\ No newline at end of file
... ...
device-subpackage/device-detail/device-position.vue renamed from deviceSubPage/deviceDetailPage/devicePosition.vue
  1 +.command-detail {
  2 + padding: 5rpx 30rpx;
  3 + height: 100vh;
  4 + background-color: #f8f9fa;
  5 +
  6 + .detail-top {
  7 + height: 118rpx;
  8 + width: 690rpx;
  9 + display: flex;
  10 + align-items: center;
  11 + background-color: #fff;
  12 + color: #333;
  13 + border-radius: 20rpx;
  14 + font-size: 15px;
  15 + margin-top: 30rpx;
  16 + padding: 30rpx;
  17 + }
  18 +
  19 + .detail {
  20 + background-color: #fff;
  21 + margin-top: 30rpx;
  22 + border-radius: 20rpx;
  23 + width: 690rpx;
  24 +
  25 + .detail-item {
  26 + padding: 30rpx;
  27 + display: flex;
  28 + align-items: center;
  29 +
  30 + .detail-label {
  31 + color: #333;
  32 + font-size: 15px;
  33 + }
  34 +
  35 + .detail-value {
  36 + color: #666;
  37 + font-size: 14px;
  38 + margin-left: 30rpx;
  39 + }
  40 + }
  41 + }
  42 +
  43 + .command {
  44 + margin: 30rpx 0;
  45 + }
  46 + }
\ No newline at end of file
... ...
  1 +.command-record {
  2 + padding: 0 30rpx;
  3 + background: #f8f9fa;
  4 +
  5 + .filter-button {
  6 + font-size: 12px;
  7 + width: 160rpx;
  8 + height: 64rpx;
  9 + border-radius: 32rpx;
  10 + display: flex;
  11 + justify-content: center;
  12 + align-items: center;
  13 + background: #f0f1f2;
  14 + color: #666;
  15 +
  16 + image {
  17 + width: 28rpx;
  18 + height: 28rpx;
  19 + margin-left: 4rpx;
  20 + }
  21 + }
  22 + }
  23 +
  24 + .list-item {
  25 + width: 690rpx;
  26 + background-color: #fff;
  27 + border-radius: 20rpx;
  28 + margin: 20rpx auto;
  29 + color: #333;
  30 +
  31 + .item {
  32 + .delivered-color {
  33 + color: blue;
  34 + }
  35 +
  36 + padding: 30rpx;
  37 +
  38 + view {
  39 + font-size: 14px;
  40 + margin-bottom: 10rpx;
  41 + }
  42 +
  43 + .time {
  44 + margin-top: 20rpx;
  45 + color: #999;
  46 + }
  47 +
  48 + .item-first {
  49 + display: flex;
  50 + justify-content: space-between;
  51 + align-items: center;
  52 + font-size: 15px;
  53 + font-weight: 500;
  54 + align-items: center;
  55 +
  56 + .item-right {
  57 + display: flex;
  58 + justify-content: center;
  59 + align-items: center;
  60 + width: 104rpx;
  61 + height: 36rpx;
  62 + font-size: 10px;
  63 + border-radius: 20rpx;
  64 + }
  65 +
  66 + .item-fail {
  67 + color: #848383;
  68 + background-color: #84838325;
  69 + }
  70 +
  71 + .item.success {
  72 + color: #00c9a7;
  73 + background-color: #00c9a725;
  74 + }
  75 + }
  76 + }
  77 + }
  78 +
  79 + .filter {
  80 + padding: 0 30rpx;
  81 +
  82 + .filter-title {
  83 + text-align: center;
  84 + margin-top: 14px;
  85 + font-size: 16px;
  86 + font-weight: 700;
  87 + }
  88 +
  89 + .button-group {
  90 + display: flex;
  91 + margin-top: 40rpx;
  92 + justify-content: space-between;
  93 +
  94 + view {
  95 + width: 330rpx;
  96 + }
  97 + }
  98 +
  99 + .command-time-text {
  100 + color: #333;
  101 + font-size: 14px;
  102 + font-weight: 700;
  103 + }
  104 + }
\ No newline at end of file
... ...
device-subpackage/device-detail/static/modal.css renamed from deviceSubPage/deviceDetailPage/styles/modal.css
  1 +/* ==================
  2 +模态窗口 采用colorsui的部分样式
  3 +==================== */
  4 +
1 5 .cu-modal {
2 6 position: fixed;
3 7 top: 0;
... ... @@ -18,7 +22,7 @@
18 22 }
19 23
20 24 .cu-modal::before {
21   - content: "\200B";
  25 + content: '\200B';
22 26 display: inline-block;
23 27 height: 100%;
24 28 vertical-align: middle;
... ... @@ -89,8 +93,8 @@
89 93 transform: translateX(0%);
90 94 }
91 95
92   -.cu-modal .cu-dialog>.cu-bar:first-child .action {
  96 +.cu-modal .cu-dialog > .cu-bar:first-child .action {
93 97 min-width: 100rpx;
94 98 margin-right: 0;
95 99 min-height: 100rpx;
96   -}
  100 +}
\ No newline at end of file
... ...
device-subpackage/device-detail/static/realtime-data.scss renamed from deviceSubPage/deviceDetailPage/tabDetail/realtimeData.vue
1   -<template>
2   - <view class="realtime-page">
3   - <view class="item" v-for="(item, index) in recordList" :key="index">
4   - <view class="item-top">
5   - <view>{{ item.key }}</view>
6   - <view class="item-value">{{ item.value }}</view>
7   - </view>
8   - <view class="item-time">{{ item.time }}</view>
9   - </view>
10   - <mescroll-empty v-if="!recordList.length" />
11   - </view>
12   -</template>
13   -
14   -<script>
15   -export default {
16   - props: {
17   - recordList: {
18   - type: Array,
19   - default: () => []
20   - }
21   - }
22   -};
23   -</script>
24   -
25   -<style lang="scss" scoped>
26   -.realtime-page {
27   - .item {
28   - margin: 30rpx;
29   - padding: 30rpx;
30   - border-radius: 20rpx;
31   - background-color: #fff;
32   - height: 160rpx;
33   - width: 690rpx;
34   - .item-top {
35   - display: flex;
36   - justify-content: space-between;
37   - color: #333;
38   - font-size: 16px;
39   - font-family: PingFangSC-Medium, PingFang SC;
40   - font-weight: bold;
41   - .item-value {
42   - font-weight: bold;
43   - }
44   - }
45   - .item-time {
46   - margin-top: 4rpx;
47   - font-size: 13px;
48   - color: #999;
49   - }
50   - }
51   -}
52   -</style>
  1 +.realtime-page {
  2 + .item {
  3 + margin: 30rpx;
  4 + padding: 30rpx;
  5 + border-radius: 20rpx;
  6 + background-color: #fff;
  7 + height: 160rpx;
  8 + width: 690rpx;
  9 + .item-top {
  10 + display: flex;
  11 + justify-content: space-between;
  12 + color: #333;
  13 + font-size: 16px;
  14 + font-family: PingFangSC-Medium, PingFang SC;
  15 + font-weight: bold;
  16 + .item-value {
  17 + font-weight: bold;
  18 + }
  19 + }
  20 + .item-time {
  21 + margin-top: 4rpx;
  22 + font-size: 13px;
  23 + color: #999;
  24 + }
  25 + }
  26 +}
\ No newline at end of file
... ...
1   -<template>
2   - <!-- 单向没有响应失败状态 -->
3   - <!-- 响应类型 -->
4   - <view class="command-record">
5   - <view class="filter-button" @click="openSearchDialog">
6   - <text>筛选</text>
7   - <image src="../../../static/shaixuan.png" />
8   - </view>
9   -
10   - <mescroll-uni ref="mescrollRef" @init="mescrollInit" :down="downOption" @down="downCallback" @up="upCallback"
11   - height="700px">
12   - <view @click="openCommandDetail(item)" class="list-item" v-for="(item, index) in list" :key="index">
13   - <view class="item">
14   - <view class="item-first">
15   - <text>{{ item.deviceName }}</text>
16   - <view v-if="!item.request.oneway">
17   - <view class="item-right item-success" v-if="item.response">响应成功</view>
18   - <view class="item-right item-fail" v-else>响应失败</view>
19   - </view>
20   - </view>
21   - <view>
22   - 命令类型:
23   - <text style="margin-left: 16rpx;">{{ item.additionalInfo.cmdType===1?'服务':'自定义' }}</text>
24   - </view>
25   - <view v-if="item.statusName">
26   - 命令状态:
27   - <text :style="{
28   - color:
29   - item.status == 'EXPIRED'
30   - ? 'red'
31   - : item.status == 'DELIVERED'
32   - ? 'blue'
33   - : item.status == 'QUEUED'
34   - ? '#00C9A7'
35   - : item.status == 'TIMEOUT'
36   - ? 'red'
37   - : item.status == 'SENT'
38   - ? '#00C9A7'
39   - : ''
40   - }" style="margin-left: 16rpx;">
41   - {{ item.statusName }}
42   - </text>
43   - </view>
44   - <view class="item-first">
45   - <view>
46   - 响应类型:
47   - <text style="margin-left: 16rpx;">{{ !item.request.oneway?'双向':'单向' }}</text>
48   - </view>
49   - <view class="time">{{ format(item.createTime) }}</view>
50   - </view>
51   - </view>
52   - </view>
53   - </mescroll-uni>
54   - <!-- 告警筛选 -->
55   - <u-popup @close="close" closeable bgColor="#fff" :show="show" mode="bottom" :round="20"
56   - @touchmove.stop.prevent="disabledScroll">
57   - <view class="filter" @touchmove.stop.prevent="disabledScroll">
58   - <view class="filter-title"><text>筛选条件</text></view>
59   - <FilterItem :filterList="issueStatus" title="下发状态"
60   - @clickTag="currentIndex => handleClickTag(currentIndex, issueStatus)"></FilterItem>
61   - <view class="button-group">
62   - <view>
63   - <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="重置"
64   - @click="resetFilter"></u-button>
65   - </view>
66   - <view>
67   - <u-button color="#3388ff" shape="circle" text="确认" @click="confirmFilter"></u-button>
68   - </view>
69   - </view>
70   - </view>
71   - </u-popup>
72   - <u-calendar :show="showCalendar" mode="range" @confirm="calendarConfirm" @close="calendarClose" startText="开始时间"
73   - endText="结束时间" confirmDisabledText="请选择日期" :formatter="formatter"></u-calendar>
74   - </view>
75   -</template>
76   -<script>
77   - import FilterItem from '@/pages/device/FilterItem.vue';
78   - import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
79   - import {
80   - formatToDate
81   - } from '@/plugins/utils.js';
82   - import {
83   - debounce
84   - } from '@/plugins/throttle.js';
85   - export default {
86   - mixins: [MescrollMixin],
87   - components: {
88   - FilterItem
89   - },
90   - props: {
91   - tbDeviceId: {
92   - type: String,
93   - default: ''
94   - }
95   - },
96   - data() {
97   - return {
98   - show: false,
99   - list: [],
100   - total: '',
101   - timeData: {
102   - selectTime: '',
103   - getTimeGap: ''
104   - },
105   - showCalendar: false,
106   - issueStatus: [{
107   - checked: true,
108   - name: '全部',
109   - type: ''
110   - },
111   - {
112   - checked: false,
113   - name: '响应成功',
114   - type: 'SUCCESSFUL'
115   - },
116   - {
117   - checked: false,
118   - name: '发送成功',
119   - type: 'DELIVERED'
120   - },
121   - {
122   - checked: false,
123   - name: '已过期',
124   - type: 'EXPIRED'
125   - },
126   - {
127   - checked: false,
128   - name: '响应失败',
129   - type: 'FAILED'
130   - }
131   - ],
132   - downOption: {
133   - auto: false //是否在初始化后,自动执行downCallback; 默认true
134   - },
135   - page: {
136   - num: 0,
137   - size: 10
138   - }
139   - };
140   - },
141   - methods: {
142   - /*下拉刷新的回调 */
143   - downCallback() {
144   - //联网加载数据
145   - this.list = [];
146   - this.page.num = 1;
147   - this.loadData(this.page.num, {
148   - tbDeviceId: this.tbDeviceId
149   - });
150   - },
151   - format(date) {
152   - return formatToDate(date, 'YYYY-MM-DD HH:mm:ss');
153   - },
154   - disabledScroll() {
155   - return;
156   - },
157   - /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
158   - upCallback() {
159   - //联网加载数据
160   - this.page.num += 1;
161   - this.loadData(this.page.num, {
162   - tbDeviceId: this.tbDeviceId
163   - });
164   - },
165   - //获取告警数据
166   - loadData(pageNo, params = {}) {
167   - let httpData = {
168   - ...params,
169   - page: pageNo,
170   - pageSize: 10
171   - };
172   - uni.$u.http
173   - .get('/yt/rpc', {
174   - params: httpData,
175   - custom: {
176   - load: false
177   - }
178   - })
179   - .then(res => {
180   - this.total = res.total;
181   - uni.stopPullDownRefresh();
182   - //方法一(推荐): 后台接口有返回列表的总页数 totalPage
183   - this.mescroll.endByPage(res.items.length, res.total); //必传参数(当前页的数据个数, 总页数)
184   - if (pageNo == 1) {
185   - this.list = res.items;
186   - } else {
187   - this.list = this.list.concat(res.items);
188   - }
189   - })
190   - .catch(() => {
191   - //联网失败, 结束加载
192   - this.mescroll.endErr();
193   - });
194   - },
195   - handleClickTag(currentIndex, list) {
196   - list.map((item, index) => {
197   - item.checked = index === currentIndex;
198   - });
199   - },
200   - resetFilter() {
201   - const {
202   - issueStatus
203   - } = this;
204   - issueStatus.forEach(item => item.map((item, index) => (item.checked = index === 0)));
205   - },
206   - close() {
207   - this.show = false;
208   - },
209   - openSearchDialog() {
210   - this.show = true;
211   - },
212   - hideKeyboard() {
213   - uni.hideKeyboard();
214   - },
215   - calendarConfirm(e) {
216   - this.showCalendar = false;
217   - this.timeData.selectTime = `${e[0]} / ${e[e.length - 1]}`;
218   - },
219   - confirmFilter() {
220   - const issueStatus = this.issueStatus.find(item => item.checked);
221   - this.loadData(1, {
222   - status: issueStatus.type ? issueStatus.type : undefined,
223   - tbDeviceId: this.tbDeviceId
224   - });
225   - this.show = false;
226   - },
227   - calendarClose() {
228   - this.showCalendar = false;
229   - },
230   - openCommandDetail(item) {
231   - uni.navigateTo({
232   - url: '/deviceSubPage/deviceDetailPage/tabDetail/CommandDetail?data=' + JSON.stringify(item)
233   - });
234   - }
235   - }
236   - };
237   -</script>
238   -
239   -<style lang="scss" scoped>
240   - .command-record {
241   - padding: 0 30rpx;
242   - background: #f8f9fa;
243   -
244   - .filter-button {
245   - font-size: 12px;
246   - width: 160rpx;
247   - height: 64rpx;
248   - border-radius: 32rpx;
249   - display: flex;
250   - justify-content: center;
251   - align-items: center;
252   - background: #f0f1f2;
253   - color: #666;
254   -
255   - image {
256   - width: 28rpx;
257   - height: 28rpx;
258   - margin-left: 4rpx;
259   - }
260   - }
261   - }
262   -
263   - .list-item {
264   - width: 690rpx;
265   - background-color: #fff;
266   - border-radius: 20rpx;
267   - margin: 20rpx auto;
268   - color: #333;
269   -
270   - .item {
271   - .delivered-color {
272   - color: blue;
273   - }
274   -
275   - padding: 30rpx;
276   -
277   - view {
278   - font-size: 14px;
279   - margin-bottom: 10rpx;
280   - }
281   -
282   - .time {
283   - margin-top: 20rpx;
284   - color: #999;
285   - }
286   -
287   - .item-first {
288   - display: flex;
289   - justify-content: space-between;
290   - align-items: center;
291   - font-size: 15px;
292   - font-weight: 500;
293   - align-items: center;
294   -
295   - .item-right {
296   - display: flex;
297   - justify-content: center;
298   - align-items: center;
299   - width: 104rpx;
300   - height: 36rpx;
301   - font-size: 10px;
302   - border-radius: 20rpx;
303   - }
304   -
305   - .item-fail {
306   - color: #848383;
307   - background-color: #84838325;
308   - }
309   -
310   - .item.success {
311   - color: #00c9a7;
312   - background-color: #00c9a725;
313   - }
314   - }
315   - }
316   - }
317   -
318   - .filter {
319   - padding: 0 30rpx;
320   -
321   - .filter-title {
322   - text-align: center;
323   - margin-top: 14px;
324   - font-size: 16px;
325   - font-weight: 700;
326   - }
327   -
328   - .button-group {
329   - display: flex;
330   - margin-top: 40rpx;
331   - justify-content: space-between;
332   -
333   - view {
334   - width: 330rpx;
335   - }
336   - }
337   - }
338   -</style>
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/FilterItem.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>
  1 +const rules = {
  2 + 'feedbackInfo.title': {
  3 + type: 'string',
  4 + required: true,
  5 + message: '请输入主题',
  6 + trigger: ['blur', 'change']
  7 + },
  8 + 'feedbackInfo.name': {
  9 + type: 'string',
  10 + required: true,
  11 + message: '请输入姓名',
  12 + trigger: ['blur', 'change']
  13 + },
  14 + 'feedbackInfo.message': {
  15 + type: 'string',
  16 + required: true,
  17 + message: '请输入意见反馈',
  18 + trigger: ['blur', 'change']
  19 + }
  20 +}
  21 +export {
  22 + rules
  23 +}
\ No newline at end of file
... ...
feedback-subpackage/feedback/feedback.vue renamed from feedBackSubPage/feedback/feedback.vue
1 1 <template>
2 2 <view class="feedback-page">
3   - <view style="overflow-y: scroll;overflow: hidden;height: 1500rpx;">
  3 + <view class="feedback-container">
4 4 <!-- 公共组件-每个页面必须引入 -->
5 5 <public-module></public-module>
6 6 <view class="form-page">
7 7 <u--form labelPosition="left" :model="feedbackData" :rules="rules" ref="myfeedBackFormRef">
8   - <u-form-item required label="主题" prop="feedbackInfo.title" borderBottom ref="item1">
  8 + <u-form-item required label="主题" prop="feedbackInfo.title" borderBottom>
9 9 <u--input placeholder="请输入主题" v-model="feedbackData.feedbackInfo.title" border="none">
10 10 </u--input>
11 11 </u-form-item>
12   - <u-form-item required label="姓名" prop="feedbackInfo.name" borderBottom ref="item1">
  12 + <u-form-item required label="姓名" prop="feedbackInfo.name" borderBottom>
13 13 <u--input placeholder="请输入姓名" v-model="feedbackData.feedbackInfo.name" border="none"></u--input>
14 14 </u-form-item>
  15 + <u-form-item required label="反馈" prop="feedbackInfo.message" borderBottom>
  16 + <u--textarea placeholder="请输入反馈信息" v-model="feedbackData.feedbackInfo.message" count>
  17 + </u--textarea>
  18 + </u-form-item>
  19 + <view class="feed-back-text upload-text">上传图片(最多6张)</view>
15 20 <view class="info">
16 21 <view class="info-contain">
17   - <u-form-item required label="反馈" prop="feedbackInfo.message" borderBottom ref="item1">
18   - <u--textarea placeholder="请输入反馈信息" v-model="feedbackData.feedbackInfo.message" count>
19   - </u--textarea>
20   - </u-form-item>
21   - </view>
22   - </view>
23   - <view class="feed-back-text" style="margin: 15px 0px 0px -16rpx;">上传图片(最多6张)</view>
24   - <view class="info" style="margin-top: 15rpx;background: rgba(1, 1, 1, 0);">
25   - <view class="info-contain">
26   - <u-form-item label="图片" prop="feedbackInfo.images" borderBottom ref="item1">
  22 + <u-form-item label="图片" prop="feedbackInfo.images" borderBottom>
27 23 <u-upload :capture="capture" :fileList="fileList1" @afterRead="afterRead"
28 24 @delete="deletePic" name="1" multiple :maxCount="6"></u-upload>
29 25 </u-form-item>
30 26 </view>
31   - <view style="width:427rpx;margin:0 auto;">
  27 + <view class="info-button">
  28 + <!-- #ifdef MP -->
  29 + <u-button class="buttonSty button-sty" shape="circle" type="primary" text="提交"
  30 + customStyle="margin-top: 280rpx" @click="submit"></u-button>
  31 + <!-- #endif -->
  32 + <!-- #ifdef APP-PLUS -->
32 33 <u-button class="buttonSty button-sty" shape="circle" type="primary" text="提交"
33   - customStyle="margin-top: 129rpx" @click="submit"></u-button>
  34 + customStyle="margin-top: 880rpx" @click="submit"></u-button>
  35 + <!-- #endif -->
34 36 </view>
35 37 </view>
36 38 </u--form>
... ... @@ -53,6 +55,8 @@
53 55 mapState
54 56 } from 'vuex';
55 57 import api from '@/api/index.js'
  58 + import { rules } from './config/data.js'
  59 + import { UPLOAD_FILE_SIZE } from '@/constant/index.js'
56 60
57 61 export default {
58 62 data() {
... ... @@ -67,27 +71,7 @@
67 71 }
68 72 },
69 73 fileList1: [],
70   - rules: {
71   - 'feedbackInfo.title': {
72   - type: 'string',
73   - required: true,
74   - message: '请输入主题',
75   - trigger: ['blur', 'change']
76   - },
77   - 'feedbackInfo.name': {
78   - type: 'string',
79   - required: true,
80   - message: '请输入姓名',
81   - trigger: ['blur', 'change']
82   - },
83   - 'feedbackInfo.message': {
84   - type: 'string',
85   - required: true,
86   - message: '请输入意见反馈',
87   - trigger: ['blur', 'change']
88   - },
89   -
90   - },
  74 + rules
91 75 };
92 76 },
93 77 onReady() {
... ... @@ -111,7 +95,7 @@
111 95 let lists = [].concat(event.file);
112 96 let fileListLen = this[`fileList${event.name}`].length;
113 97 lists.map(item => {
114   - if (item.size > 5242880) {
  98 + if (item.size > UPLOAD_FILE_SIZE) {
115 99 this[`fileList${event.name}`].push({
116 100 ...item,
117 101 status: 'error',
... ... @@ -127,7 +111,7 @@
127 111 });
128 112 for (let i = 0; i < lists.length; i++) {
129 113 const judgeImageSize = lists[0].size
130   - if (judgeImageSize > 5242880) {
  114 + if (judgeImageSize > UPLOAD_FILE_SIZE) {
131 115 return uni.$u.toast('图片限定5M')
132 116 } else {
133 117 const result = await this.uploadFilePromise(lists[i].url);
... ... @@ -153,7 +137,7 @@
153 137 // #endif
154 138 if (!token) return uni.$u.toast('请登录后上传图片');
155 139 return new Promise((resolve, reject) => {
156   - let a = uni.uploadFile({
  140 + uni.uploadFile({
157 141 url: `${baseUrl.baseUrl}/yt/oss/upload`,
158 142 filePath: url,
159 143 name: 'file',
... ... @@ -186,7 +170,7 @@
186 170 .validate()
187 171 .then(async res => {
188 172 if (res) {
189   - let httpData = {
  173 + let data = {
190 174 title: this.feedbackData.feedbackInfo.title,
191 175 name: this.feedbackData.feedbackInfo.name,
192 176 images: this.feedbackData.feedbackInfo.images.length == 0 ? '' : JSON
... ... @@ -194,11 +178,8 @@
194 178 this.feedbackData.feedbackInfo.images),
195 179 message: this.feedbackData.feedbackInfo.message
196 180 };
197   - const res = await api.feedbackApi.postFeedBackApi(httpData)
198   - uni.showToast({
199   - title: '意见反馈提交成功~',
200   - icon: 'none'
201   - });
  181 + const res = await api.feedbackApi.postFeedBackApi(data)
  182 + uni.$u.toast('意见反馈提交成功~');
202 183 setTimeout(() => {
203 184 uni.navigateBack();
204 185 }, 500);
... ... @@ -213,58 +194,7 @@
213 194 </script>
214 195
215 196 <style lang="scss" scoped>
216   - .feedback-page {
217   - min-height: 100vh;
218   - background-color: #f8f9fa;
219   - padding-top: 9rpx;
220   - padding-left: 27rpx;
221   - overflow-y: scroll;
222   - overflow: hidden;
223   - }
224   -
225   - .form-page {
226   - width: 700rpx;
227   - background-color: #ffffff;
228   - border-radius: 10px;
229   - margin-top: 20rpx;
230   - padding-left: 15rpx;
231   - padding: 0 20rpx;
232   - height: 500rpx;
233   -
234   - .info {
235   - width: 700rpx;
236   - background-color: #ffffff;
237   - border-radius: 10px;
238   - margin-top: 100rpx;
239   - height: 256rpx;
240   - margin-left: -20rpx;
241   -
242   - .info-contain {
243   - margin: 0rpx 27rpx;
244   -
245   - /deep/ .u-line {
246   - display: none !important;
247   - }
248   -
249   - /deep/ .u-form-item__body__left__content__label {
250   - display: none !important;
251   - }
252   -
253   - /deep/.u-form-item__body__right {
254   - margin-left: -106rpx;
255   - }
256   -
257   - /deep/.u-textarea--radius {
258   - border: none !important;
259   - }
260   - }
261   - }
262   -
263   - /deep/.u-button--primary {
264   - background-color: #377DFF !important;
265   - border-color: #377DFF !important;
266   - }
267   - }
  197 + @import "./static/feedback.scss";
268 198
269 199 //#ifndef MP
270 200 .buttonSty {
... ... @@ -272,4 +202,4 @@
272 202 }
273 203
274 204 //#endif
275   -</style>
  205 +</style>
\ No newline at end of file
... ...
  1 +.feedback-page {
  2 + min-height: 100vh;
  3 + background-color: #f8f9fa;
  4 + padding-top: 9rpx;
  5 + padding-left: 27rpx;
  6 + overflow-y: scroll;
  7 + overflow: hidden;
  8 + .feedback-container{
  9 + overflow-y: scroll;
  10 + overflow: hidden;
  11 + height: 1500rpx;
  12 + }
  13 + }
  14 +
  15 + .form-page {
  16 + width: 700rpx;
  17 + background-color: #ffffff;
  18 + border-radius: 10px;
  19 + margin-top: 20rpx;
  20 + padding: 0 40rpx;
  21 + height: 860rpx;
  22 + .upload-text{
  23 + margin: 15px 0px 0px -16rpx;
  24 + }
  25 +
  26 + .info {
  27 + width: 700rpx;
  28 + background-color: #ffffff;
  29 + border-radius: 10px;
  30 + margin-top: 100rpx;
  31 + height: 256rpx;
  32 + margin-left: -20rpx;
  33 + margin-top: 15rpx;
  34 + background: rgba(1, 1, 1, 0);
  35 +
  36 + .info-contain {
  37 + margin: 0rpx 27rpx;
  38 +
  39 + /deep/ .u-line {
  40 + display: none !important;
  41 + }
  42 +
  43 + /deep/ .u-form-item__body__left__content__label {
  44 + display: none !important;
  45 + }
  46 +
  47 + /deep/.u-form-item__body__right {
  48 + margin-left: -106rpx;
  49 + }
  50 +
  51 + /deep/.u-textarea--radius {
  52 + border: none !important;
  53 + }
  54 + }
  55 + .info-button{
  56 + width:427rpx;
  57 + margin:0 auto;
  58 + }
  59 + }
  60 +
  61 + /deep/.u-button--primary {
  62 + background-color: #377DFF !important;
  63 + border-color: #377DFF !important;
  64 + }
  65 + }
\ No newline at end of file
... ...
  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.phoneNumber" type="number" placeholder="请输入手机号码"
  12 + border="bottom"></u-input></view>
  13 + <view class="form-row">
  14 + <u-input type="number" v-model="loginForm.code" 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 {mapState,mapMutations,mapActions} from 'vuex';
  33 + import {useShowToast,useNavigateTo,useReLaunch} from '@/plugins/utils.js'
  34 + import api from '@/api'
  35 +
  36 + export default {
  37 + data() {
  38 + return {
  39 + loginForm: {
  40 + phoneNumber: '',
  41 + code: ''
  42 + },
  43 + readonly: false,
  44 + codeText: '发送验证码',
  45 + };
  46 + },
  47 + methods: {
  48 + ...mapMutations(['setUserInfo']),
  49 + ...mapActions(['updateBadgeTotal']),
  50 + //验证码按钮文字状态
  51 + codeCountdownText() {
  52 + const _this = this;
  53 + this.readonly = true;
  54 + this.codeText = '60s后重新获取';
  55 + var s = 60;
  56 + clear = setInterval(() => {
  57 + s--;
  58 + _this.codeText = s + 's后重新获取';
  59 + if (s <= 0) {
  60 + clearInterval(clear);
  61 + _this.codeText = '发送验证码';
  62 + _this.readonly = false;
  63 + }
  64 + }, 1000);
  65 + },
  66 + //获取验证码
  67 + async getVerifyCode() {
  68 + const phoneRegular = /^1\d{10}$/;
  69 + if (this.readonly) {
  70 + useShowToast('验证码已发送~')
  71 + }
  72 + if (!this.loginForm.phoneNumber) {
  73 + return useShowToast('请输入手机号~')
  74 + }
  75 + if (!phoneRegular.test(this.loginForm.phoneNumber)) {
  76 + return useShowToast('手机号格式不正确~')
  77 + }
  78 + // 获取验证码接口
  79 + await api.loginApi.postPhoneCodeApi(this.loginForm.phoneNumber)
  80 + this.codeCountdownText(); //开始倒计时
  81 + },
  82 + async onSubmit() {
  83 + const phoneRegular = /^1\d{10}$/;
  84 + const verifyCodeReg = /^\d{6}$/;
  85 + const validateValue = Object.values(this.loginForm)
  86 + if (!validateValue[0]) return uni.$u.toast("请输入手机号码~");
  87 + if (!validateValue[1]) return uni.$u.toast("请输入验证码~");
  88 + if (!phoneRegular.test(validateValue[0])) return uni.$u.toast("手机号格式不正确~");
  89 + if (!verifyCodeReg.test(validateValue[1])) return uni.$u.toast("验证码格式不正确~");
  90 + const res = await api.loginApi.postPhoneLoginApi(this.loginForm)
  91 + if (res) {
  92 + // 储存登录信息
  93 + let tokenInfo = {
  94 + refreshToken: res.refreshToken,
  95 + isToken: res.token
  96 + };
  97 + let userInfo = {
  98 + ...tokenInfo,
  99 + token: true, //token用于判断是否登录
  100 + isThirdLogin: false
  101 + };
  102 + if (userInfo.token) {
  103 + this.setUserInfo(userInfo);
  104 + }
  105 + useShowToast('登录成功~').then(async (res) => {
  106 + this.saveUserInfo();
  107 + await this.getAlarmTotalData();
  108 + useReLaunch("/pages/index/index")
  109 + });
  110 + }
  111 + },
  112 + async getAlarmTotalData() {
  113 + const res = await await api.loginApi.getAlarmTotalApi()
  114 + if (!res) return
  115 + //异步实时更新告警徽标数
  116 + this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
  117 + },
  118 + async saveUserInfo() {
  119 + //储存个人信息
  120 + const res = await api.loginApi.setUserInfoApi()
  121 + if (!res) return
  122 + this.setUserInfo(res);
  123 + },
  124 + openAccountFunc() {
  125 + useNavigateTo('../public/login')
  126 + }
  127 + }
  128 + };
  129 +</script>
  130 +
  131 +<style lang="scss" scoped>
  132 + @import './static/code.scss';
  133 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <view class="find-password-page">
  3 + <public-module></public-module>
  4 + <view class="top u-flex">
  5 + <view @click="showPhone" :style="{ color: phoneNumberColor }" class="item">1.验证手机号码</view>
  6 + <view :style="{ color: passwordColor }" class="item">2.设置新密码</view>
  7 + </view>
  8 + <view v-if="!nextStatus" class="login-body">
  9 + <view class="login-phone">
  10 + <view class="form-row">
  11 + <u-input v-model="forgetForm.phone" type="number" placeholder="请输入手机号码" border="bottom" />
  12 + </view>
  13 + <view class="form-row">
  14 + <u-input type="number" v-model="forgetForm.verifyCode" placeholder="请输入验证码" border="bottom">
  15 + <template slot="suffix">
  16 + <view @click="getVerifyCode" class="verify-code">{{ codeText }}</view>
  17 + </template>
  18 + </u-input>
  19 + </view>
  20 + <button class="submit" size="default" @click="onNextSubmit"><text style="color:#fff">下一步</text></button>
  21 + </view>
  22 + </view>
  23 + <view v-else class="login-body">
  24 + <view class="login-phone">
  25 + <view class="form-row u-flex">
  26 + <u-input v-model="forgetForm.password" :password="showPassword" placeholder="请设置6-20位新的登录密码" border="bottom">
  27 + <template slot="suffix">
  28 + <view @click="showPasswordMode" style="padding: 10rpx">
  29 + <u-icon width="18" height="14" :name="passwordModeIcon"></u-icon>
  30 + </view>
  31 + </template>
  32 + </u-input>
  33 + </view>
  34 + <view class="form-row u-flex">
  35 + <u-input v-model="forgetForm.repeatPassword" :password="showPasswordRepeat" placeholder="请再次输入新的登录密码" border="bottom">
  36 + <template slot="suffix">
  37 + <view @click="showPasswordModeRepeat" style="padding: 10rpx">
  38 + <u-icon width="18" height="14" :name="passwordModeRepeatIcon"></u-icon>
  39 + </view>
  40 + </template>
  41 + </u-input>
  42 + </view>
  43 + <button class="submit" size="default" @click="onSubmit"><text style="color:#fff">确定</text></button>
  44 + </view>
  45 + </view>
  46 + </view>
  47 +</template>
  48 +
  49 +<script>
  50 + import api from '@/api/index.js'
  51 + import { loginPasswordReg } from '@/plugins/utils.js'
  52 + import { useShowToast,useShowModal,useReLaunch } from '@/plugins/utils.js'
  53 +
  54 + var clear;
  55 + export default {
  56 + data() {
  57 + return {
  58 + forgetForm:{
  59 + phone: '',
  60 + verifyCode: '',
  61 + password: '',
  62 + repeatPassword: '',
  63 + },
  64 + readonly: false,
  65 + codeText: '发送验证码',
  66 + nextStatus: false,
  67 + showPassword: true,
  68 + showPasswordRepeat: true
  69 + };
  70 + },
  71 + computed:{
  72 + phoneNumberColor(){
  73 + return !this.nextStatus ? '#0079fe' : ''
  74 + },
  75 + passwordColor(){
  76 + return !this.nextStatus ? '' : '#0079fe'
  77 + },
  78 + passwordModeIcon(){
  79 + return this.showPassword ? '/static/eye-hide.png' : '/static/eye.png'
  80 + },
  81 + passwordModeRepeatIcon(){
  82 + return this.showPasswordRepeat ? '/static/eye-hide.png' : '/static/eye.png'
  83 + }
  84 + },
  85 + methods: {
  86 + //验证码按钮文字状态
  87 + verifyCodeCountDown() {
  88 + const _this = this;
  89 + this.readonly = true;
  90 + this.codeText = '60s后重新获取';
  91 + var s = 60;
  92 + clear = setInterval(() => {
  93 + s--;
  94 + _this.codeText = s + 's后重新获取';
  95 + if (s <= 0) {
  96 + clearInterval(clear);
  97 + _this.codeText = '发送验证码';
  98 + _this.readonly = false;
  99 + }
  100 + }, 1000);
  101 + },
  102 + //获取验证码
  103 + getVerifyCode() {
  104 + const phoneRegular = /^1\d{10}$/;
  105 + if (this.readonly) {
  106 + useShowToast('验证码已发送~')
  107 + }
  108 + console.log(this.forgetForm.phone);
  109 + if (!this.forgetForm.phone) {
  110 + return useShowToast('请输入手机号~')
  111 + }
  112 + if (!phoneRegular.test(this.forgetForm.phone)) {
  113 + return useShowToast('手机号格式不正确~')
  114 + }
  115 + api.loginApi.postCodeApi(this.forgetForm.phone)
  116 + .then(res => {
  117 + this.verifyCodeCountDown(); //开始倒计时
  118 + })
  119 + },
  120 + onNextSubmit() {
  121 + const phoneRegular = /^1\d{10}$/;
  122 + const verifyCodeReg=/^\d{6}$/;
  123 + const validateValue = Object.values(this.forgetForm)
  124 + if(!validateValue[0]) return uni.$u.toast("请输入手机号码~");
  125 + if(!validateValue[1]) return uni.$u.toast("请输入验证码~");
  126 + if(!phoneRegular.test(validateValue[0])) return uni.$u.toast("手机号格式不正确~");
  127 + if(!verifyCodeReg.test(validateValue[1])) return uni.$u.toast("验证码格式不正确~");
  128 + this.nextStatus = true;
  129 + },
  130 + showPhone() {
  131 + this.nextStatus = false;
  132 + },
  133 + onSubmit() {
  134 + const validateValue = Object.values(this.forgetForm)
  135 + if(!validateValue[2]) return uni.$u.toast("请输入密码~");
  136 + if(!validateValue[3]) return uni.$u.toast("请输入密码~");
  137 + if(!loginPasswordReg.test(validateValue[2])) return useShowModal('密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~')
  138 + if(!loginPasswordReg.test(validateValue[3])) return useShowModal('密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~')
  139 + if (validateValue[2] !== validateValue[3]) return uni.$u.toast('两次输入密码不一致');
  140 + let httpData = {
  141 + password: this.forgetForm.password,
  142 + phoneNumber: this.forgetForm.phone,
  143 + userId: this.forgetForm.verifyCode
  144 + };
  145 + const res = api.loginApi.postResetCodeApi(this.forgetForm.phone, httpData)
  146 + if (res) {
  147 + useShowToast( '重置密码成功~').then(res => {
  148 + useReLaunch('/publicLoginSubPage/public/login')
  149 + });
  150 + }
  151 + },
  152 + showPasswordMode() {
  153 + this.showPassword = !this.showPassword;
  154 + },
  155 + showPasswordModeRepeat() {
  156 + this.showPasswordRepeat = !this.showPasswordRepeat;
  157 + }
  158 + }
  159 + };
  160 +</script>
  161 +
  162 +<style lang="scss" scoped>
  163 + @import './static/findPassword.scss';
  164 +</style>
... ...
login-subpackage/other/set.vue renamed from publicLoginSubPage/other/set.vue
... ... @@ -5,42 +5,40 @@
5 5 <!-- #ifdef MP-WEIXIN -->
6 6 <view @click="upAvatar" class="u-flex set-main">
7 7 <view class="main-image">
8   - <image class="image" :src="avatar || '../../static/logo.png'"></image>
  8 + <image class="image" :src="uploadImage"></image>
9 9 </view>
10 10 <view class="main-right-image">
11   - <image class="image" src="../../static/arrow-right.png"></image>
  11 + <image class="image" src="/static/arrow-right.png"></image>
12 12 </view>
13 13 </view>
14 14 <!-- #endif -->
15 15 <!-- #ifdef APP-PLUS -->
  16 + <!-- 这里上传写两道的原因是,如果是app端,则兼容打开相机拍照上传 -->
16 17 <view @click="upAppAvatar" class="u-flex set-main">
17 18 <view class="main-image">
18   - <image class="image" :src="avatar || '../../static/logo.png'"></image>
  19 + <image class="image" :src="uploadImage"></image>
19 20 </view>
20 21 <view class="main-right-image">
21   - <image class="image" src="../../static/arrow-right.png"></image>
  22 + <image class="image" src="/static/arrow-right.png"></image>
22 23 </view>
23 24 </view>
24 25 <!-- #endif -->
25 26 <view class="u-m-t-20 basic-text"><text class="text">基本资料</text></view>
26 27 <view class="basic-main">
27 28 <u--form labelPosition="left" :model="myInfoModel" ref="myForm">
28   - <u-form-item labelWidth="80px" label="真实姓名" prop="realName" borderBottom ref="item1">
  29 + <u-form-item labelWidth="80px" label="真实姓名" prop="realName" borderBottom>
29 30 <u--input placeholder="请输入真实姓名" v-model="myInfoModel.realName" border="none"></u--input>
30 31 </u-form-item>
31   - <u-form-item labelWidth="80px" label="手机号码" prop="phoneNumber" borderBottom ref="item1">
  32 + <u-form-item labelWidth="80px" label="手机号码" prop="phoneNumber" borderBottom>
32 33 <u--input placeholder="请输入手机号码" v-model="myInfoModel.phoneNumber" border="none"></u--input>
33 34 </u-form-item>
34   - <u-form-item labelWidth="80px" label="用户账号" prop="username" borderBottom ref="item1">
  35 + <u-form-item labelWidth="80px" label="用户账号" prop="username" borderBottom>
35 36 <u--input disabled placeholder="请输入用户账号 " v-model="myInfoModel.username" border="none"></u--input>
36 37 </u-form-item>
37   - <u-form-item labelWidth="80px" label="邮箱地址" prop="email" borderBottom ref="item1">
  38 + <u-form-item labelWidth="80px" label="邮箱地址" prop="email" borderBottom>
38 39 <u--input placeholder="请输入邮箱地址" v-model="myInfoModel.email" border="none"></u--input>
39 40 </u-form-item>
40   - <u-form-item @click="
41   - showDate = true;
42   - hideKeyboard();
43   - " labelWidth="80px" label="有效期" prop="accountExpireTime" ref="item1">
  41 + <u-form-item @click="hideKeyboard" labelWidth="80px" label="有效期" prop="accountExpireTime">
44 42 <u--input v-model="myInfoModel.accountExpireTime" placeholder="请选择有效期" border="none"></u--input>
45 43 <u-datetime-picker :formatter="formatter" :show="showDate" :value="datetime" mode="dateTime"
46 44 closeOnClickOverlay @confirm="dateConfirm" @cancel="dateClose" @close="dateClose">
... ... @@ -61,7 +59,7 @@
61 59 class="un-bind-text">确认</text></button>
62 60 </view>
63 61 </view>
64   - <!-- #ifdef MP -->
  62 + <!-- #ifdef MP-WEIXIN -->
65 63 <view class="u-m-t-40"><text style="visibility: hidden;">#</text></view>
66 64 <!-- #endif -->
67 65 <!-- 解绑账号 -->
... ... @@ -85,11 +83,10 @@
85 83 mapMutations
86 84 } from 'vuex';
87 85 import baseUrl from '@/config/baseUrl.js';
88   - import {
89   - mapState
90   - } from 'vuex';
  86 + import {mapState} from 'vuex';
91 87 import api from '@/api/index.js'
92 88 import permission from '@/js_sdk/wa-permission/permission.js'
  89 + import {useShowModal,useUploadFile,useChooseImage,useFileValidate} from '@/plugins/utils.js'
93 90
94 91 export default {
95 92 data() {
... ... @@ -114,7 +111,6 @@
114 111 email: '',
115 112 accountExpireTime: ''
116 113 },
117   -
118 114 rules: {
119 115 phoneNumber: [{
120 116 required: true,
... ... @@ -153,14 +149,10 @@
153 149 },
154 150 onLoad(e) {
155 151 if (e.data !== null) {
156   - let params = JSON.parse(e.data);
  152 + let params = JSON.parse(decodeURIComponent(e.data));
  153 + for (let i in this.myInfoModel) Reflect.set(this.myInfoModel, i, params.data[i])
157 154 this.info = params;
158   - this.myInfoModel.realName = params.data.realName;
159   - this.myInfoModel.phoneNumber = params.data.phoneNumber;
160   - this.myInfoModel.username = params.data.username;
161   - this.myInfoModel.email = params.data.email;
162   - this.myInfoModel.accountExpireTime = params.data.accountExpireTime;
163   - this.avatar = params.data.avatar == undefined ? '../../static/logo.png' : params.data.avatar;
  155 + this.avatar = params.data.avatar == undefined ? '/static/logo.png' : params.data.avatar;
164 156 this.id = params.data.userId;
165 157 }
166 158 },
... ... @@ -168,24 +160,22 @@
168 160 let getOpenId = getApp().globalData.openId;
169 161 if (getOpenId) {
170 162 this.openIds = getOpenId;
171   - console.log('OPenid', this.openIds);
172 163 }
173 164 },
174 165 computed: {
175   - ...mapState(['userInfo'])
  166 + uploadImage() {
  167 + return this.avatar || '/static/logo.png'
  168 + }
176 169 },
177 170 methods: {
  171 + ...mapState(['userInfo']),
  172 + ...mapMutations(['setUserInfo', 'emptyUserInfo']),
178 173 modify() {
179   - uni.showModal({
180   - title: '需要下列权限才可以正常使用',
181   - content: this.modify_content,
182   - confirmText: '前往开启',
183   - success: function(res) {
184   - if (res.confirm) {
185   - permission.gotoAppPermissionSetting(); //动态修改权限
186   - }
  174 + useShowModal(this.modify_content, '需要下列权限才可以正常使用', '前往开启').then((res) => {
  175 + if (res.confirm) {
  176 + permission.gotoAppPermissionSetting(); //动态修改权限
187 177 }
188   - });
  178 + })
189 179 },
190 180 async requestAndroidPermission(permissionID, e) {
191 181 let resp = await permission.requestAndroidPermission(permissionID, e);
... ... @@ -193,14 +183,14 @@
193 183 this.modify();
194 184 } else if (resp == 1) {
195 185 if (e == 1) {
196   - this.chooseImage1()
  186 + this.nativeCameraChooseFile()
197 187 } else {
198   - this.chooseImage2()
  188 + this.chooseLocalAlbum()
199 189 }
200 190 }
201 191 },
202   - ...mapMutations(['setUserInfo', 'emptyUserInfo']),
203   - chooseImage1() {
  192 + //使用相机上传
  193 + nativeCameraChooseFile() {
204 194 let token;
205 195 token = this.userInfo.isToken || uni.getStorageSync('userInfo').isToken || undefined;
206 196 // #ifdef H5
... ... @@ -213,32 +203,26 @@
213 203 cmr.captureImage(
214 204 (path) => {
215 205 this.showSelectType = false
216   - uni.uploadFile({
217   - url: `${baseUrl.baseUrl}/yt/oss/upload`,
218   - filePath: path,
219   - name: 'file',
220   - header: {
  206 + useUploadFile(`${baseUrl.baseUrl}/yt/oss/upload`, path, 'file', {}, {
221 207 'content-type': 'multipart/form-data',
222 208 Authorization: 'Bearer ' + token
223   - },
224   - formData: {},
225   - success: res => {
  209 + })
  210 + .then(res => {
226 211 let objImage = JSON.parse(res.data);
227 212 this.avatar = objImage.fileStaticUri;
228 213 uni.$u.toast('头像上传成功');
229   - }
230   - });
  214 + })
231 215 },
232 216 function(error) {
233   - console.log('Capture image failed: ' + error.message);
  217 + uni.$u.toast('Capture image failed: ' + error.message);
234 218 }, {
235 219 resolution: res,
236 220 format: fmt
237 221 }
238 222 );
239 223 },
240   - //选择相册
241   - chooseImage2() {
  224 + //选择相册上传
  225 + chooseLocalAlbum() {
242 226 let token;
243 227 token = this.userInfo.isToken || uni.getStorageSync('userInfo').isToken || undefined;
244 228 // #ifdef H5
... ... @@ -247,26 +231,19 @@
247 231 if (!token) return uni.$u.toast('请登录后上传图片');
248 232 plus.gallery.pick(
249 233 (path) => {
250   - console.log(path)
251 234 this.showSelectType = false
252   - uni.uploadFile({
253   - url: `${baseUrl.baseUrl}/yt/oss/upload`,
254   - filePath: path,
255   - name: 'file',
256   - header: {
  235 + useUploadFile(`${baseUrl.baseUrl}/yt/oss/upload`, path, 'file', {}, {
257 236 'content-type': 'multipart/form-data',
258 237 Authorization: 'Bearer ' + token
259   - },
260   - formData: {},
261   - success: res => {
  238 + })
  239 + .then(res => {
262 240 let objImage = JSON.parse(res.data);
263 241 this.avatar = objImage.fileStaticUri;
264 242 uni.$u.toast('头像上传成功');
265   - }
266   - });
  243 + })
267 244 },
268 245 function(e) {
269   - console.log('取消选择图片');
  246 + uni.$u.toast('您取消选择图片');
270 247 }, {
271 248 filter: 'image'
272 249 }
... ... @@ -281,9 +258,7 @@
281 258 };
282 259 api.loginApi.deleteBindApi(httpData).then(res => {
283 260 if (res) {
284   - uni.showToast({
285   - title: '解绑成功'
286   - });
  261 + uni.$u.toast('解绑成功');
287 262 this.showBind = false;
288 263 this.isJudgeBindBtn = false;
289 264 uni.reLaunch({
... ... @@ -291,9 +266,7 @@
291 266 });
292 267 this.emptyUserInfo();
293 268 } else {
294   - uni.showToast({
295   - title: '解绑失败'
296   - });
  269 + uni.$u.toast('解绑失败');
297 270 this.showBind = false;
298 271 }
299 272 });
... ... @@ -334,52 +307,22 @@
334 307 token = window.sessionStorage.getItem('userInfo').isToken;
335 308 // #endif
336 309 if (!token) return uni.$u.toast('请登录后上传图片');
337   - uni.chooseImage({
  310 + useChooseImage({
338 311 count: 1,
339 312 sizeType: ['compressed'],
340   - sourceType: ['camera', 'album'],
341   - success: res => {
342   - const tempFilePaths = res.tempFilePaths;
343   - //限制上传的图片大小不超过5M
344   - let resSize = res.tempFiles[0].size;
345   - if (resSize > 5242880) {
346   - uni.showToast({
347   - title: '上传的图片大小不能超过5M',
348   - icon: 'none',
349   - duration: 2000,
350   - mask: true
351   - });
352   - return;
353   - }
354   - //获取图片扩展名
355   - const fileTxt = res.tempFilePaths[0].split('.').pop();
356   - const judgeType = fileTxt == 'jpg' || fileTxt == 'jpeg' || fileTxt == 'png';
357   - if (!judgeType) {
358   - uni.showToast({
359   - title: '请上传指定图片类型(jpg、jpeg、png)',
360   - icon: 'none',
361   - duration: 2000,
362   - mask: true
363   - });
364   - return;
365   - }
366   - uni.uploadFile({
367   - url: `${baseUrl.baseUrl}/yt/oss/upload`,
368   - filePath: tempFilePaths[0],
369   - name: 'file',
370   - header: {
371   - 'content-type': 'multipart/form-data',
372   - Authorization: 'Bearer ' + token
373   - },
374   - formData: {},
375   - success: res => {
376   - let objImage = JSON.parse(res.data);
377   - this.avatar = objImage.fileStaticUri;
378   - uni.$u.toast('头像上传成功');
379   - }
380   - });
381   - },
382   - });
  313 + sourceType: ['camera', 'album']
  314 + }).then((res) => {
  315 + useFileValidate(res[0], 5242880)
  316 + useUploadFile(`${baseUrl.baseUrl}/yt/oss/upload`, res[0].path, 'file', {}, {
  317 + 'content-type': 'multipart/form-data',
  318 + Authorization: 'Bearer ' + token
  319 + })
  320 + .then(res => {
  321 + let objImage = JSON.parse(res.data);
  322 + this.avatar = objImage.fileStaticUri;
  323 + uni.$u.toast('头像上传成功');
  324 + })
  325 + })
383 326 },
384 327 onSubmitFunc() {
385 328 let httpData = {
... ... @@ -395,15 +338,10 @@
395 338 const resp = await api.loginApi.postPersonalInfoApi(httpData)
396 339 if (res) {
397 340 this.setUserInfo(resp);
398   - uni.showToast({
399   - title: '更新个人资料成功~',
400   - icon: 'none'
  341 + uni.navigateBack({
  342 + delta: 1
401 343 });
402   - setTimeout(() => {
403   - uni.navigateBack({
404   - delta: 1
405   - });
406   - }, 500);
  344 + uni.$u.toast('更新个人资料成功~');
407 345 }
408 346 })
409 347 .catch(errors => {
... ... @@ -417,7 +355,6 @@
417 355 this.showDate = false;
418 356 this.myInfoModel.userInfo.accountExpireTime = uni.$u.timeFormat(e.value, 'yyyy-mm-dd hh:MM:ss');
419 357 },
420   - //格式化日期
421 358 formatter(type, value) {
422 359 if (type === 'year') {
423 360 return `${value}年`;
... ... @@ -438,6 +375,7 @@
438 375 },
439 376 //隐藏输入框
440 377 hideKeyboard() {
  378 + this.showDate = true;
441 379 uni.hideKeyboard();
442 380 }
443 381 }
... ... @@ -465,4 +403,4 @@
465 403 width: 663rpx !important;
466 404 }
467 405 }
468   -</style>
  406 +</style>
\ No newline at end of file
... ...
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   -.f__login {
9   - padding: 48rpx 32rpx;
10   - border-radius: 18rpx 18rpx 0 0;
11   - z-index: 99;
12   - position: relative;
13   -}
14   -
15   -.loginPhone {
16   - .phone-main {
17   - .text {
18   - font-size: 22px;
19   - color: #3a4759;
20   - position: relative;
21   - z-index: 9999;
22   - font-family: PingFangSC-Semibold, PingFang SC;
23   - font-weight: 600;
24   - }
25   - }
26   - .form-row {
27   - margin-top: 30rpx;
28   - .getvcode {
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   - position: relative;
40   - background: linear-gradient(90deg, #5dc2fc 0%, #377dff 100%);
41   - border-radius: 46px;
42   - .text {
43   - color: #ffffff;
44   - }
45   - }
46   - .account-style {
47   - flex-direction: row;
48   - margin-top: 48rpx;
49   - justify-content: space-between;
50   - .content {
51   - color: #999999;
52   - font-size: 13px;
53   - }
54   - }
  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 + }
55 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   -.f__login {
23   - padding: 0 30rpx;
24   - .loginPhone {
25   - .form-row {
26   - margin-top: 30rpx;
27   - .getvcode {
28   - font-family: PingFangSC-Regular, PingFang SC;
29   - font-weight: 400;
30   - font-size: 14px;
31   - color: #6299ff;
32   - }
33   - }
34   -
35   - .submit {
36   - margin-top: 60rpx;
37   - width: 100%;
38   - background: #377dff;
39   - border-radius: 48px;
40   - }
41   - }
  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 + }
42 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="{
3   - backgroundImage:
4   - 'url(' +
5   - (mpOwnConfig.bg !== undefined ? mpOwnConfig.bg : `${defaultLogo}`) +
6   - ')',
7   - }">
8   - <!-- 公共组件-每个页面必须引入 -->
9   - <public-module></public-module>
10   - <view class="u-flex login-main">
11   - <view class="content">
12   - <view class="hello login-text-muted">您好,</view>
13   - <view style="width: 587rpx;" class="text-clip hello-welcome login-text-muted">欢迎来到{{
14   - mpOwnConfig.name !== undefined ? mpOwnConfig.name : "ThingsKit"
15   - }}!</view>
16   - </view>
17   - </view>
18   -
19   - <view class="f__login">
20   - <view class="loginPhone">
21   - <view class="form-row u-flex">
22   - <u-input :adjust-position="false" v-model="loginForm.username" type="text" placeholder="请输入登录账号"
23   - border="bottom" />
24   - </view>
25   - <view class="form-row u-flex">
26   - <u-input :adjust-position="false" v-model="loginForm.password" :password="showPassword"
27   - placeholder="请输入登录密码" border="bottom">
28   - <template slot="suffix">
29   - <view @click="showPasswordMode" style="padding: 10rpx">
30   - <u-icon width="18" height="14" :name="
31   - showPassword ? '/static/eye-hide.png' : '/static/eye.png'
32   - "></u-icon>
33   - </view>
34   - </template>
35   - </u-input>
36   - </view>
37   - <button class="submit" size="default" @click="onSubmitFunc">
38   - <text class="text">登录</text>
39   - </button>
40   - <view class="u-flex row-item">
41   - <view class="row-phone login-text-gray" @click="openCodeFunc">手机验证码登录</view>
42   - <view class="row-reset login-text-gray" @click="findPassrordFunc">忘记密码</view>
43   - </view>
44   - <view class="u-flex link-login">
45   - <!-- #ifdef MP-WEIXIN -->
46   - <view class="link-text login-text-gray">第三方账号登录</view>
47   - <view style="height: 20rpx"></view>
48   - <button class="link-image" @tap="handleWenxinAuthorization">
49   - <image class="image" src="../../static/weixin.png" mode="aspectFill"></image>
50   - </button>
51   - <!-- #endif -->
52   - <!-- #ifdef APP-PLUS -->
53   - <!-- <view class="link-text login-text-gray">第三方账号登录</view>
54   - <view style="height: 20rpx"></view>
55   - <button class="link-image" @click="handleAppPlusAuthorization">
56   - <image class="image" src="../../static/weixin.png" mode="aspectFill"></image>
57   - </button> -->
58   - <!-- #endif -->
59   - </view>
60   - </view>
61   - </view>
62   - </view>
63   -</template>
64   -
65   -<script>
66   - import {
67   - mapMutations,
68   - mapActions,
69   - mapState
70   - } from "vuex";
71   - import api from '@/api'
72   - import { loginPasswordReg } from '@/plugins/utils.js'
73   -
74   - export default {
75   - data() {
76   - return {
77   - loginForm: {
78   - username: "",
79   - password: "",
80   - },
81   - showPassword: true,
82   - code: "",
83   - openid: "",
84   - mpOwnConfig: {},
85   - defaultLogo: "/static/login.png",
86   - };
87   - },
88   - onLoad() {
89   - //#ifdef MP-WEIXIN
90   - wx.login({
91   - success: (res) => {
92   - if (res.code) {
93   - this.code = res.code;
94   - //这里获取openid
95   - } else {
96   - return;
97   - }
98   - },
99   - });
100   - //#endif
101   - uni.setStorageSync('getConfiguration', {
102   - isConfiguration: false
103   - });
104   - },
105   - computed: {
106   - ...mapState(["plateInfo"]),
107   - },
108   - onShow() {
109   - uni.setStorageSync('getConfiguration', {
110   - isConfiguration: false
111   - });
112   - this.getPlateForm();
113   - },
114   - methods: {
115   - //获取平台定制信息
116   - async getPlateForm() {
117   - const res = await api.loginApi.getPlateCustomApi()
118   - if (res) {
119   - this.mpOwnConfig = {
120   - bg: res.background,
121   - logo: res.logo,
122   - name: res.name,
123   - };
124   - }
125   - },
126   - ...mapMutations(["setUserInfo", "setPlateInfo"]),
127   - ...mapActions(["updateBadgeTotal"]),
128   - //微信授权登录
129   - //#ifdef MP-WEIXIN
130   - handleWenxinAuthorization() {
131   - wx.getUserProfile({
132   - desc: "微信第三方授权",
133   - success: async (reswenxin) => {
134   - if (
135   - reswenxin.errMsg === "getUserProfile:ok" &&
136   - reswenxin.encryptedData
137   - ) {
138   - //获取用户信息
139   - let obj = {
140   - avatarUrl: reswenxin.userInfo.avatarUrl,
141   - thirdUserId: this.openid,
142   - };
143   - const res = await api.loginApi.getThirdLoginApi(this.code)
144   - if (res.token == "" || (res.token == null && res.thirdUserId)) {
145   - //需要绑定
146   - let userInfo = {
147   - isThirdLogin: true, //用于判断是否是第三方登录并且需要绑定账号
148   - avatar: obj.avatarUrl,
149   - thirdUserId: res.thirdUserId,
150   - };
151   - this.setUserInfo(userInfo);
152   - //设置全局变量openId
153   - getApp().globalData.openId = res.thirdUserId;
154   - uni.reLaunch({
155   - url: "../../pages/personal/personal",
156   - });
157   - } else {
158   - // 不需要绑定,直接第三方登录使用
159   - let resObj = {
160   - refreshToken: res.refreshToken,
161   - isToken: res.token,
162   - };
163   - let userInfo = {
164   - ...resObj,
165   - token: true, //token用于判断是否登录
166   - isThirdLoginAndNoDind: true, //用于判断是否是第三方登录并且不需要绑定账号
167   - thirdUserId: res.thirdUserId,
168   - };
169   - //设置全局变量openId
170   - getApp().globalData.openId = res.thirdUserId;
171   - if (userInfo.token) {
172   - this.setUserInfo(userInfo);
173   - }
174   - uni
175   - .showToast({
176   - title: "第三方账号登录成功",
177   - icon: "none",
178   - })
179   - .then(async (res) => {
180   - this.saveUserInfo();
181   - await this.getAlarmTotalData();
182   - uni.reLaunch({
183   - url: "/pages/personal/personal",
184   - });
185   - });
186   - }
187   - }
188   - },
189   - fail: (res) => {
190   - //拒绝授权
191   - return;
192   - },
193   - });
194   - },
195   - //#endif
196   - //#ifdef APP-PLUS
197   - // handleAppPlusAuthorization() {
198   - // var that = this;
199   - // uni.getProvider({
200   - // service: 'oauth',
201   - // success: function(res) {
202   - // if (~res.provider.indexOf('weixin')) {
203   - // uni.login({
204   - // provider: 'weixin',
205   - // success: function(loginRes) {
206   - // // loginRes 包含access_token,expires_in,openid,unionid等信息
207   - // uni.showToast({
208   - // title: "第三方账号登录成功",
209   - // icon: "none",
210   - // })
211   - // },
212   - // fail: function(res) {
213   - // // that.$refs.uToast.show({
214   - // // title: '微信登录失败',
215   - // // type: 'warning'
216   - // // });
217   - // }
218   - // });
219   - // }
220   - // }
221   - // });
222   - // },
223   - //#endif
224   - async saveUserInfo() {
225   - const userInfoRes = await api.loginApi.setUserInfoApi()
226   - const plateInfoRes = await api.loginApi.setPlateInfoApi()
227   - if (userInfoRes) {
228   - this.setUserInfo(userInfoRes);
229   - }
230   - if (plateInfoRes) {
231   - this.setPlateInfo(plateInfoRes);
232   - }
233   - },
234   - async getAlarmTotalData() {
235   - const res = await api.loginApi.getAlarmTotalApi()
236   - if (res) {
237   - this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
238   - }
239   - },
240   - async onSubmitFunc() {
241   - if (this.loginForm.username == "") {
242   - return uni.$u.toast("请输入登录账号~");
243   - }
244   - if (this.loginForm.password == "") {
245   - uni.showToast({
246   - title: "请输入登录密码~",
247   - icon: "none",
248   - });
249   - return;
250   - } else if (!loginPasswordReg.test(this.loginForm.password)) {
251   - uni.showModal({
252   - title: "提示",
253   - content: "密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~",
254   - showCancel: false,
255   - });
256   - return;
257   - }
258   - const res = await api.loginApi.postLoginApi(this.loginForm)
259   - if (res) {
260   - // 储存登录信息
261   - let resObj = {
262   - refreshToken: res.refreshToken,
263   - isToken: res.token,
264   - };
265   - let userInfo = {
266   - ...resObj,
267   - token: true, //token用于判断是否登录
268   - isThirdLogin: false,
269   - isThirdLoginAndNoDind: false,
270   - };
271   - if (userInfo.token) {
272   - this.setUserInfo(userInfo);
273   - }
274   - uni
275   - .showToast({
276   - title: "登录成功~",
277   - icon: "none",
278   - })
279   - .then(async (res) => {
280   - await this.saveUserInfo();
281   - await this.getAlarmTotalData();
282   - uni.reLaunch({
283   - url: "/pages/personal/personal",
284   - });
285   - });
286   - }
287   - },
288   - openCodeFunc() {
289   - uni.navigateTo({
290   - url: "../other/code",
291   - });
292   - },
293   - findPassrordFunc() {
294   - uni.navigateTo({
295   - url: "../other/findPassword",
296   - });
297   - },
298   - showPasswordMode() {
299   - this.showPassword = !this.showPassword;
300   - },
301   - },
302   - };
303   -</script>
304   -
305   -<style lang="scss" scoped>
306   - @import "./static/login.scss";
307   -
308   - /deep/ button {
309   - background: rgba(0, 0, 0, 0);
310   - }
311   -</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 {mapMutations,mapActions,mapState} from "vuex";
  51 + import api from '@/api'
  52 + import {loginPasswordReg,useReLaunch,useShowToast,useShowModal,useNavigateTo} from '@/plugins/utils.js'
  53 +
  54 + export default {
  55 + data() {
  56 + return {
  57 + loginForm: {
  58 + username: "fengtao",
  59 + password: "Aa123456@",
  60 + },
  61 + showPassword: true,
  62 + code: "",
  63 + openid: "",
  64 + mpOwnConfig: {},
  65 + defaultLogo: "/static/login.png",
  66 + };
  67 + },
  68 + onLoad() {
  69 + //#ifdef MP-WEIXIN
  70 + wx.login({
  71 + success: (res) => {
  72 + if (!res.code) return
  73 + this.code = res.code;
  74 + },
  75 + });
  76 + //#endif
  77 + uni.setStorageSync('getConfiguration', {
  78 + isConfiguration: false
  79 + });
  80 + this.getPlateForm();
  81 + },
  82 + computed: {
  83 + passwordIcon() {
  84 + return this.showPassword ? '/static/eye-hide.png' : '/static/eye.png'
  85 + },
  86 + setHeadTitle() {
  87 + return this.mpOwnConfig.name !== undefined ? this.mpOwnConfig.name : "ThingsKit"
  88 + },
  89 + setLoginBg() {
  90 + return 'url(' + (this.mpOwnConfig.bg !== undefined ? this.mpOwnConfig.bg : `${this.defaultLogo}`) + ')'
  91 + }
  92 + },
  93 + onShow() {
  94 + uni.setStorageSync('getConfiguration', {
  95 + isConfiguration: false
  96 + });
  97 + },
  98 + methods: {
  99 + ...mapState(["plateInfo"]),
  100 + ...mapMutations(["setUserInfo", "setPlateInfo"]),
  101 + ...mapActions(["updateBadgeTotal"]),
  102 + //获取平台定制信息
  103 + async getPlateForm() {
  104 + const res = await api.loginApi.getPlateCustomApi()
  105 + if (!res) return
  106 + this.mpOwnConfig = {
  107 + bg: res.background,
  108 + logo: res.logo,
  109 + name: res.name,
  110 + };
  111 + },
  112 + saveLoginInfo(res, isThirdLoginAndNoDind, isThirdLogin, toastText) {
  113 + let tokenInfo = {
  114 + refreshToken: res.refreshToken,
  115 + isToken: res.token,
  116 + };
  117 + let userInfo = {
  118 + ...tokenInfo,
  119 + token: true, //token用于判断是否登录
  120 + isThirdLoginAndNoDind, //用于判断是否是第三方登录并且不需要绑定账号
  121 + isThirdLogin, //用于判断是否是第三方登录并且需要绑定账号
  122 + thirdUserId: res.thirdUserId,
  123 + };
  124 + //设置全局变量openId
  125 + getApp().globalData.openId = res.thirdUserId;
  126 + if (userInfo.token) {
  127 + this.setUserInfo(userInfo);
  128 + }
  129 + useShowToast(toastText).then(async (res) => {
  130 + this.saveUserInfo();
  131 + await this.getAlarmTotalData();
  132 + useReLaunch("../../pages/index/index")
  133 + });
  134 + },
  135 + //微信授权登录
  136 + //#ifdef MP-WEIXIN
  137 + handleWenxinAuthorization() {
  138 + wx.getUserProfile({
  139 + desc: "微信第三方授权",
  140 + success: async (reswenxin) => {
  141 + if (
  142 + reswenxin.errMsg === "getUserProfile:ok" &&
  143 + reswenxin.encryptedData
  144 + ) {
  145 + //获取用户信息
  146 + let wenxinUserInfo = {
  147 + avatarUrl: reswenxin.userInfo.avatarUrl,
  148 + thirdUserId: this.openid,
  149 + };
  150 + const res = await api.loginApi.getThirdLoginApi(this.code)
  151 + if (!res.token && res.thirdUserId) {
  152 + //需要进行三方绑定
  153 + let userInfo = {
  154 + isThirdLogin: true, //用于判断是否是第三方登录并且需要绑定账号
  155 + avatar: wenxinUserInfo.avatarUrl,
  156 + thirdUserId: res.thirdUserId,
  157 + };
  158 + this.setUserInfo(userInfo);
  159 + //设置全局变量openId
  160 + getApp().globalData.openId = res.thirdUserId;
  161 + useReLaunch("/pages/index/index")
  162 + } else {
  163 + // 不需要绑定,直接第三方登录使用
  164 + this.saveLoginInfo(res, true, null, "第三方账号登录成功")
  165 + }
  166 + }
  167 + },
  168 + fail: (res) => {
  169 + //拒绝授权
  170 + return;
  171 + },
  172 + });
  173 + },
  174 + //#endif
  175 + async saveUserInfo() {
  176 + const userInfoRes = await api.loginApi.setUserInfoApi()
  177 + const plateInfoRes = await api.loginApi.setPlateInfoApi()
  178 + Promise.all([userInfoRes, plateInfoRes]).then(res => {
  179 + this.setUserInfo(res[0])
  180 + this.setPlateInfo(res[1])
  181 + })
  182 + },
  183 + async getAlarmTotalData() {
  184 + const res = await api.loginApi.getAlarmTotalApi()
  185 + if (!res) return
  186 + this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
  187 + },
  188 + async onSubmitFunc() {
  189 + const validateValue = Object.values(this.loginForm)
  190 + if (!validateValue[0]) return uni.$u.toast("请输入登录账号~");
  191 + if (!validateValue[1]) return uni.$u.toast("请输入登录密码~");
  192 + if (!loginPasswordReg.test(validateValue[1])) return useShowModal(
  193 + "密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~", )
  194 + const res = await api.loginApi.postLoginApi(this.loginForm)
  195 + if (res) {
  196 + // 储存登录信息
  197 + this.saveLoginInfo(res, false, false, "登录成功~")
  198 + }
  199 + },
  200 + openCodeFunc() {
  201 + useNavigateTo("../other/code")
  202 + },
  203 + findPassrordFunc() {
  204 + useNavigateTo("../other/find-password")
  205 + },
  206 + showPasswordMode() {
  207 + this.showPassword = !this.showPassword;
  208 + },
  209 + },
  210 + };
  211 +</script>
  212 +
  213 +<style lang="scss" scoped>
  214 + @import "./static/login.scss";
  215 +
  216 + /deep/ button {
  217 + background: rgba(0, 0, 0, 0);
  218 + }
  219 +</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   -.f__login {
32   - padding: 8rpx 32rpx;
33   - border-radius: 18rpx 18rpx 0 0;
34   - z-index: 99;
35   - position: relative;
36   -}
37   -
38   -.loginPhone {
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 }
... ...
... ... @@ -4,7 +4,7 @@ import App from './App'
4 4 // 工具
5 5 import '@/plugins/utils.js';
6 6
7   -//权限配置中心
  7 +//服务端路径配置中心
8 8 import base from '@/config/baseUrl'
9 9 Vue.prototype.$base = base;
10 10
... ... @@ -22,13 +22,15 @@ import f_show_modal from '@/components/module/show_modal/f_show_modal.js'
22 22 Vue.use(f_show_modal)
23 23 // #endif
24 24
25   -// uview
  25 +// uview
26 26 import uView from '@/uni_modules/uview-ui'
27 27 Vue.use(uView)
28 28
29   -// 公共组件
  29 +// 公共组件注册
30 30 import publicModule from "@/components/common/public-module.vue";
31 31 Vue.component("public-module", publicModule);
  32 +import headerSearch from "@/components/common/header-search.vue";
  33 +Vue.component("header-search", headerSearch);
32 34
33 35 Vue.config.productionTip = false
34 36 App.mpType = 'app'
... ...
... ... @@ -26,24 +26,12 @@
26 26 }
27 27 },
28 28 {
29   - "path": "pages/device/org/org",
30   - "style": {
31   - "navigationBarTitleText": "组织筛选"
32   - }
33   - },
34   - {
35 29 "path": "pages/alarm/alarm",
36 30 "style": {
37 31 "navigationBarTitleText": "告警"
38 32 }
39 33 },
40 34 {
41   - "path": "pages/alarm/org/org",
42   - "style": {
43   - "navigationBarTitleText": "组织筛选"
44   - }
45   - },
46   - {
47 35 "path": "pages/personal/personal",
48 36 "style": {
49 37 "navigationBarTitleText": "个人中心",
... ... @@ -51,55 +39,55 @@
51 39 }
52 40 },
53 41 {
54   - "path": "pages/index/camera/camera",
  42 + "path": "pages/index/components/camera/camera",
55 43 "style": {
56 44 "navigationBarTitleText": "查看摄像头"
57 45 }
58 46 },
59 47 {
60   - "path": "pages/index/camera/org/org",
61   - "style": {
62   - "navigationBarTitleText": "组织筛选"
63   - }
64   - }, {
65   - "path": "pages/index/configuration/configuration",
  48 + "path": "pages/index/components/configuration/configuration",
66 49 "style": {
67 50 "navigationBarTitleText": "查看组态"
68 51 }
69   -
70 52 },
71 53 {
72   - "path": "pages/index/configuration/configurationDetail",
  54 + "path": "pages/index/components/configuration/configuration-detail",
73 55 "style": {
74 56 "navigationBarTitleText": "组态详情"
75 57 }
  58 + },
  59 + {
  60 + "path": "pages/organization/organization",
  61 + "style": {
  62 + "navigationBarTitleText": "组织筛选"
  63 + }
76 64 }
77 65 ],
78 66 "subPackages": [{
79   - "root": "alarmSubPage",
  67 + "root": "alarm-subpackage",
80 68 "pages": [{
81   - "path": "alarmDetailPage/alarmDetail",
  69 + "path": "alarm-detail/alarm-detail",
82 70 "style": {
83 71 "navigationBarTitleText": "告警详情"
84 72 }
85 73 }]
86 74 },
87 75 {
88   - "root": "deviceSubPage",
  76 + "root": "device-subpackage",
89 77 "pages": [{
90   - "path": "deviceDetailPage/deviceDetail",
  78 + "path": "device-detail/device-detail",
91 79 "style": {
92 80 "navigationBarTitleText": "设备详情"
93 81 }
94 82 },
95 83 {
96   - "path": "deviceDetailPage/tabDetail/CommandDetail",
  84 + "path": "device-detail/components/command-detail",
97 85 "style": {
98 86 "navigationBarTitleText": "命令详情"
99 87 }
100 88 },
101 89 {
102   - "path": "deviceDetailPage/devicePosition",
  90 + "path": "device-detail/device-position",
103 91 "style": {
104 92 "navigationBarTitleText": "设备地理位置"
105 93 },
... ... @@ -115,15 +103,15 @@
115 103 ]
116 104 },
117 105 {
118   - "root": "sysNotifySubPage",
  106 + "root": "sysnotify-subpackage",
119 107 "pages": [{
120   - "path": "sysNotifyPage/systemNotify",
  108 + "path": "sys-notify/system-notify",
121 109 "style": {
122 110 "navigationBarTitleText": "系统通知"
123 111 }
124 112 },
125 113 {
126   - "path": "sysNotifyPage/notifyDetail",
  114 + "path": "sys-notify/notify-detail",
127 115 "style": {
128 116 "navigationBarTitleText": "通知详情"
129 117 }
... ... @@ -131,7 +119,7 @@
131 119 ]
132 120 },
133 121 {
134   - "root": "feedBackSubPage",
  122 + "root": "feedback-subpackage",
135 123 "pages": [{
136 124 "path": "feedback/feedback",
137 125 "style": {
... ... @@ -140,7 +128,7 @@
140 128 }]
141 129 },
142 130 {
143   - "root": "publicLoginSubPage",
  131 + "root": "login-subpackage",
144 132 "pages": [{
145 133 "path": "public/login",
146 134 "style": {
... ... @@ -156,11 +144,11 @@
156 144 {
157 145 "path": "other/code",
158 146 "style": {
159   - "navigationBarTitleText": "验证码登录"
  147 + "navigationBarTitleText": "手机验证码登录"
160 148 }
161 149 },
162 150 {
163   - "path": "other/findPassword",
  151 + "path": "other/find-password",
164 152 "style": {
165 153 "navigationBarTitleText": "忘记密码"
166 154 }
... ... @@ -170,7 +158,7 @@
170 158 ],
171 159 "globalStyle": {
172 160 "navigationBarTextStyle": "black",
173   - "navigationBarTitleText": "云腾app",
  161 + "navigationBarTitleText": "ThingsKit",
174 162 "navigationBarBackgroundColor": "#FFFFFF",
175 163 "backgroundColor": "#FFFFFF",
176 164 "backgroundColorTop": "#FFFFFF"
... ... @@ -206,4 +194,4 @@
206 194 }
207 195 ]
208 196 }
209   -}
  197 +}
\ No newline at end of file
... ...
1   -<template>
2   - <view class="alert-page" :class="show ? 'pop-no-scroll' : ''">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <!-- 吸顶组件 -->
6   - <u-sticky>
7   - <view class="device-top">
8   - <view class="search">
9   - <view>
10   - <view class="search-left">
11   - <u--input prefixIcon="search" placeholder="请输入告警设备" shape="circle" @change="inputChanged">
12   - </u--input>
13   - </view>
14   - </view>
15   - <view @click="openSearchDialog" class="search-right">
16   - <text>筛选</text>
17   - <image src="../../static/shaixuan.png" />
18   - </view>
19   - </view>
20   - <u-line />
21   - <view class="org">
22   - <u-cell @click="openOrg" isLink title="组织关系" :border="false">
23   - <view slot="label" class="label" style="display: flex; align-items: center;margin-top: 20rpx;">
24   - <image src="../../static/org.png" style="width: 24rpx;height: 28rpx;"></image>
25   - <view style="margin-left: 10rpx; color: #666;">
26   - 告警数:
27   - <text style="margin-left: 20rpx;">{{ alertTotal }}</text>
28   - </view>
29   - </view>
30   - </u-cell>
31   - </view>
32   - </view>
33   - </u-sticky>
34   - <!-- 吸顶组件 -->
35   - <!-- 自带分页组件 -->
36   - <mescroll-body ref="mescrollRef" :up="upOption" @init="mescrollInit" :down="downOption" @down="downCallback"
37   - @up="upCallback">
38   - <view class="device-list">
39   - <view @click="openAlertDetail(item)" class="list-item" v-for="(item, index) in list" :key="index">
40   - <view class="u-flex item">
41   - <view class="item-text text-clip">
42   - <text class="text-bold">{{ item.deviceName == null ? '暂无数据' : item.deviceName }}</text>
43   - </view>
44   - <view class="item-text text-clip">
45   - <text
46   - class="text-muted">{{ item.details == null ? '暂无数据' : formatDetailText(item.details.data) }}</text>
47   - </view>
48   - <view class="item-text">
49   - <text class="text-muted">
50   - 告警状态:{{
51   - item.status == 'CLEARED_UNACK'
52   - ? '清除未确认'
53   - : item.status == 'ACTIVE_UNACK'
54   - ? '激活未确认'
55   - : item.status == 'CLEARED_ACK'
56   - ? '清除已确认'
57   - : '激活已确认'
58   - }}
59   - </text>
60   - </view>
61   - <view class="item-text">
62   - <text class="text-secondary">{{ item.createdTime }}</text>
63   - </view>
64   - </view>
65   - <view class="item">
66   - <view class="u-flex item-right">
67   - <image class="right-image" :src="bindImageUrl(item.severity)"></image>
68   - <view class="right-text">
69   - <text class="text-no-color" :style="[
70   - item.severity == 'CRITICAL'
71   - ? { color: '#DE4437' }
72   - : item.severity == 'MAJOR'
73   - ? { color: '#DE7337' }
74   - : item.severity == 'MINOR'
75   - ? { color: '#FFC107' }
76   - : item.severity == 'WARNING'
77   - ? { color: '#DE4437' }
78   - : { color: '#00C9A7' }
79   - ]">
80   - {{
81   - item.severity == 'CRITICAL'
82   - ? '危险'
83   - : item.severity == 'MAJOR'
84   - ? '重要'
85   - : item.severity == 'MINOR'
86   - ? '次要'
87   - : item.severity == 'WARNING'
88   - ? '警告'
89   - : '不确定'
90   - }}
91   - </text>
92   - </view>
93   - </view>
94   - </view>
95   - </view>
96   - </view>
97   - <mescroll-empty v-if="!list.length" />
98   - </mescroll-body>
99   - <!-- 自带分页组件 -->
100   - <view style="height: 20rpx"></view>
101   - <!-- 告警筛选-->
102   - <u-popup @close="close" closeable bgColor="transparent" :overlay="true" :show="show" mode="bottom">
103   - <view class="popup-page">
104   - <view class="popup-text"><text class="text">告警筛选</text></view>
105   - <view class="popup-alarm-page u-flex">
106   - <view>
107   - <view class="popup-alarm-text"><text class="text">告警状态</text></view>
108   - <view class="u-flex popup-alarm-child">
109   - <view class="alarm-text" @click="getAlertStatus(item, index)"
110   - :style="[index == current1 ? { background: 'rgba(55, 125, 255, 0.05)', border: '1rpx solid rgba(55, 125, 255, 0.3)' } : { background: '#F6F6F6' }]"
111   - v-for="(item, index) in alertStatus" :key="index">
112   - <text :class="[index == current1 ? 'select-text' : 'un-select-text']"
113   - class="text">{{ item.name }}</text>
114   - </view>
115   - </view>
116   - <view style="margin-top: 169rpx;">
117   - <view class="popup-alarm-text"><text class="text">设备类型</text></view>
118   - <view class="u-flex popup-alarm-child">
119   - <view class="alarm-text" @click="getTypeStatus(item, index)" :style="[
120   - index == current2 ? { background: 'rgba(55, 125, 255, 0.05)', border: '1rpx solid rgba(55, 125, 255, 0.3)' } : { background: '#F6F6F6' }
121   - ]" v-for="(item, index) in deviceType" :key="index">
122   - <text :class="[index == current2 ? 'select-text' : 'un-select-text']"
123   - class="text">{{ item.name }}</text>
124   - </view>
125   - </view>
126   -
127   - <view style="margin-top: 169rpx;">
128   - <view class="popup-alarm-text"><text class="text">告警等级</text></view>
129   - <view class="u-flex popup-alarm-child">
130   - <view class="alarm-text" @click="getLevelStatus(item, index)" :style="[
131   - index == current3 ? { background: 'rgba(55, 125, 255, 0.05)', border: '1rpx solid rgba(55, 125, 255, 0.3)' } : { background: '#F6F6F6' }
132   - ]" v-for="(item, index) in alertLevel" :key="index">
133   - <text :class="[index == current3 ? 'select-text' : 'un-select-text']"
134   - class="text">{{ item.name }}</text>
135   - </view>
136   - </view>
137   - <view style="margin-top: 169rpx;">
138   - <view class="popup-alarm-text"><text class="text">选择时间</text></view>
139   - <view class="u-flex popup-alarm-child">
140   - <view class="alarm-text" @click="getTimeStatus(item, index)" :style="[
141   - index == current4
142   - ? { background: 'rgba(55, 125, 255, 0.05)', border: '1rpx solid rgba(55, 125, 255, 0.3)' }
143   - : { background: '#F6F6F6' }
144   - ]" v-for="(item, index) in timeArea" :key="index">
145   - <text :class="[index == current4 ? 'select-text' : 'un-select-text']"
146   - class="text">{{ item.name }}</text>
147   - </view>
148   - </view>
149   - <view style="margin-top: 169rpx;margin-left: 22rpx;">
150   - <view class="u-flex popup-alarm-child">
151   - <view class="home-text-muted">选择日期</view>
152   - <view
153   - style="width: 623rpx;margin-left: 5rpx;margin-right: 70rpx;margin-top: 35rpx;">
154   - <uni-datetime-picker v-model="range" type="datetimerange"
155   - rangeSeparator="至" />
156   - </view>
157   - </view>
158   - </view>
159   - <view style="height: 130rpx;"></view>
160   - <view class="u-flex"
161   - style="position: fixed;bottom: 10rpx;z-index: 9999;flex-direction: row; margin-top: 128rpx; margin-left: 10rpx">
162   - <view style="width: 300rpx">
163   - <u-button @click="resetData" type="info" shape="circle" text="重置">
164   - </u-button>
165   - </view>
166   - <view style="width: 300rpx; margin-left: 46rpx">
167   - <u-button @click="queryData" type="primary" shape="circle" text="确认">
168   - </u-button>
169   - </view>
170   - </view>
171   - <view style="height: 90rpx;"></view>
172   - </view>
173   - </view>
174   - </view>
175   - </view>
176   - </view>
177   - </view>
178   - </u-popup>
179   - <f-tabbar></f-tabbar>
180   - </view>
181   -</template>
182   -
183   -<script>
184   - import fTabbar from '@/components/module/f-tabbar/f-tabbar';
185   - import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
186   - import {
187   - alertStatus,
188   - deviceType,
189   - alertLevel,
190   - timeArea
191   - } from './static/data.js';
192   - import api from '@/api/index.js'
193   -
194   - export default {
195   - mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
196   - components: {
197   - fTabbar
198   - },
199   - data() {
200   - return {
201   - range: [],
202   - alertStatusVal: '',
203   - deviceTypeVal: '',
204   - alertLevelVal: '',
205   - selectTimeVal: '',
206   - current1: 0,
207   - current2: 0,
208   - current3: 0,
209   - current4: 0,
210   - page: {
211   - num: 0,
212   - size: 10
213   - },
214   - downOption: {
215   - auto: false //是否在初始化后,自动执行downCallback; 默认true
216   - },
217   - upOption: {
218   - auto: false // 不自动加载
219   - },
220   - timeData: {
221   - selectTime: [],
222   - getTimeGapS: '',
223   - getTimeGapE: ''
224   - },
225   - show: false,
226   - list: [],
227   - alertStatus,
228   - deviceType,
229   - alertLevel,
230   - timeArea,
231   - ordId: '',
232   - detailStatus: false,
233   - alertTotal: 0,
234   - searchAlarmText: '',
235   - startTimeVa: '',
236   - endTimeVa: '',
237   - startTimeArea: '',
238   - endTimeArea: '',
239   - type: null
240   - };
241   - },
242   - onShow() {
243   - this.page.num = 1;
244   - if(getApp().getBindNot()){
245   - return
246   - }
247   - if (this.detailStatus) {
248   - this.loadData(1, null, null, null, null, null, null);
249   - }
250   - if (this.ordId) {
251   - this.loadData(1, null, null, null, null, null, this.ordId);
252   - }
253   - },
254   - onHide() {
255   - this.ordId = '';
256   - this.detailStatus = false;
257   - this.type = null;
258   - },
259   - onLoad(e) {
260   - // 隐藏原生的tabbar
261   - uni.hideTabBar();
262   - if(getApp().getBindNot()){
263   - return
264   - }
265   - if (e.type == undefined) {
266   - this.loadData(1, null, null, null, null, null, null);
267   - } else {
268   - let params = JSON.parse(e.type);
269   - if (Array.isArray(params)) {
270   - this.type = params.join(',');
271   - } else {
272   - this.type = params;
273   - }
274   - this.loadData(1, this.type, null, null, null, null, null);
275   - }
276   - },
277   - watch: {
278   - range(newval) {
279   - this.timeData.selectTime = this.range;
280   - }
281   - },
282   - methods: {
283   - moveHandle() {
284   - return false;
285   - },
286   - inputChanged(e) {
287   - this.resetData();
288   - this.topBack();
289   - this.searchAlarmText = e;
290   - this.page.num = 1;
291   - this.loadData(1, null, null, null, null, null, null, e);
292   - },
293   - getAlertStatus(e, i) {
294   - this.current1 = i;
295   - this.alertStatusVal = e.value;
296   - },
297   - getTypeStatus(e, i) {
298   - this.current2 = i;
299   - this.deviceTypeVal = e.value;
300   - },
301   - getLevelStatus(e, i) {
302   - this.current3 = i;
303   - this.alertLevelVal = e.value;
304   - },
305   - getTimeStatus(e, i) {
306   - this.current4 = i;
307   - this.selectTimeVal = e.value;
308   - let curTime = new Date();
309   - const formatS = curTime.getTime();
310   - let addMinute = new Date(curTime.setMinutes(curTime.getMinutes() - this.selectTimeVal));
311   - const formatE = addMinute.getTime();
312   - this.timeData.getTimeGapS = i>0?formatS:'';
313   - this.timeData.getTimeGapE = i>0?formatE:'';
314   - console.log(this.timeData.getTimeGapS,'this.timeData.getTimeGapS',this.timeData.getTimeGapE)
315   - },
316   - queryData() {
317   - this.topBack();
318   - this.page.num = 1;
319   - let date1 = new Date(this.timeData.selectTime[0]);
320   - let date2 = new Date(this.timeData.selectTime[1]);
321   - if (this.timeData.selectTime.length == 0) {
322   - this.startTimeVa = '';
323   - this.endTimeVa = '';
324   - } else {
325   - this.startTimeVa = date1.getTime();
326   - this.endTimeVa = date2.getTime();
327   - }
328   - if (this.timeData.getTimeGapS == '') {
329   - this.startTimeArea = '';
330   - this.endTimeArea = '';
331   - } else {
332   - this.startTimeArea = this.timeData.getTimeGapE;
333   - this.endTimeArea = this.timeData.getTimeGapS;
334   - }
335   - this.loadData(
336   - 1,
337   - this.alertStatusVal,
338   - this.startTimeVa ? this.startTimeVa : this.startTimeArea,
339   - this.endTimeVa ? this.endTimeVa : this.endTimeArea,
340   - this.alertLevelVal,
341   - this.deviceTypeVal
342   - );
343   - this.show = false;
344   - },
345   - resetData() {
346   - this.current1 = 0;
347   - this.alertStatusVal = '';
348   - this.current2 = 0;
349   - this.deviceTypeVal = '';
350   - this.current3 = 0;
351   - this.alertLevelVal = '';
352   - this.current4 = 0;
353   - this.selectTimeVal = '';
354   - this.timeData.selectTime = [];
355   - this.timeData.getTimeGapS = '';
356   - this.timeData.getTimeGapE = '';
357   - this.range = [];
358   - this.searchAlarmText = '';
359   - this.startTimeVa = '';
360   - this.endTimeVa = '';
361   - this.startTimeArea = '';
362   - this.endTimeArea = '';
363   - this.ordId = '';
364   - this.type = null;
365   - },
366   - bindImageUrl(e) {
367   - switch (e) {
368   - case 'CRITICAL':
369   - return '../../static/danger.png';
370   - break;
371   - case 'MAJOR':
372   - return '../../static/major.png';
373   - break;
374   - case 'MINOR':
375   - return '../../static/secondary.png';
376   - break;
377   - case 'WARNING':
378   - return '../../static/danger.png';
379   - break;
380   - case 'INDETERMINATE':
381   - return '../../static/noshue.png';
382   - break;
383   - default:
384   - return '';
385   - break;
386   - }
387   - },
388   - //筛选数据让它回到顶部
389   - topBack() {
390   - uni.pageScrollTo({
391   - scrollTop: 0, // 滚动到页面的目标位置 这个是滚动到顶部, 0
392   - duration: 10 // 滚动动画的时长
393   - });
394   - },
395   - /*下拉刷新的回调 */
396   - downCallback() {
397   - //联网加载数据
398   - this.list.length = 0;
399   - this.page.num = 1;
400   - this.loadData(this.page.num, null, null, null, null, null, null, null);
401   - this.resetData();
402   - },
403   - /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
404   - upCallback() {
405   - if (
406   - this.type != null ||
407   - this.alertStatusVal != '' ||
408   - this.searchAlarmText != '' ||
409   - this.deviceTypeVal != '' ||
410   - this.alertLevelVal != '' ||
411   - this.startTimeVa != '' ||
412   - this.startTimeArea != '' ||
413   - this.ordId != ''
414   - ) {
415   - //联网加载数据
416   - this.page.num += 1;
417   - this.loadData(
418   - this.page.num,
419   - this.alertStatusVal ? this.alertStatusVal : this.type,
420   - this.startTimeVa,
421   - this.endTimeVa,
422   - this.alertLevelVal,
423   - this.deviceTypeVal,
424   - this.ordId,
425   - this.searchAlarmText
426   - );
427   - } else {
428   - //联网加载数据
429   - this.page.num += 1;
430   - this.loadData(this.page.num);
431   - }
432   - },
433   - async loadData(pageNo, statusV, startTimeV, endTimeV, severityV, deviceTypeV, organizationV, alarmName) {
434   - let that = this;
435   - let httpData = {
436   - page: pageNo,
437   - pageSize: 10,
438   - status: statusV,
439   - startTime: startTimeV,
440   - endTime: endTimeV,
441   - severity: severityV,
442   - deviceType: deviceTypeV,
443   - organizationId: organizationV,
444   - deviceName: alarmName
445   - };
446   - if (statusV == '') {
447   - delete httpData.status;
448   - }
449   - if (severityV == '') {
450   - delete httpData.severity;
451   - }
452   - const res = await api.alarmApi.getAlarmApi({
453   - params: httpData,
454   - custom: {
455   - load: false
456   - }
457   - })
458   - if (res) {
459   - uni.stopPullDownRefresh();
460   - that.mescroll.endByPage(res.items.length, res.total); //必传参数(当前页的数据个数, 总页数)
461   - that.alertTotal = res.total;
462   - if (pageNo == 1) {
463   - that.list = res.items;
464   - } else {
465   - that.list = that.list.concat(res.items);
466   - }
467   - }
468   - },
469   - openOrg() {
470   - uni.navigateTo({
471   - url: './org/org'
472   - });
473   - },
474   - close() {
475   - this.show = false;
476   - },
477   - openSearchDialog() {
478   - this.show = true;
479   - this.resetData();
480   - },
481   - //跳转告警详情
482   - openAlertDetail(e) {
483   - let obj = {
484   - id: e.id,
485   - deviceName: e.deviceName,
486   - severity: e.severity,
487   - organizationName: e.organizationName,
488   - details: e.details,
489   - createdTime: e.createdTime,
490   - status: e.status,
491   - type: e.type
492   - };
493   - uni.navigateTo({
494   - url: '/alarmSubPage/alarmDetailPage/alarmDetail?data=' + JSON.stringify(obj)
495   - });
496   - },
497   - formatDetailText(e) {
498   - //去除字符串双引号
499   - const jsonStr = JSON.stringify(e);
500   - const str = jsonStr.substring(1, jsonStr.length - 1);
501   - const newStr = str.replace(/\"/g, '');
502   - return newStr;
503   - }
504   - }
505   - };
506   -</script>
507   -
508   -<style lang="scss" scoped>
509   - @import './static/alarm.scss';
510   -
511   - /deep/ .u-button--primary {
512   - background-color: #377dff !important;
513   - border-color: #377dff !important;
514   - }
515   -
516   - /deep/ .u-button--info {
517   - background-color: #e3e3e5 !important;
518   - border-color: #e3e3e5 !important;
519   - }
520   -
521   - /deep/ .u-cell__right-icon-wrap {
522   - margin-top: -55rpx !important;
523   - }
524   -
525   - /deep/ .uni-calendar--fixed {
526   - bottom: 172rpx !important;
527   - }
528   -
529   - .pop-no-scroll {
530   - overflow: hidden;
531   - position: fixed;
532   - height: 100%;
533   - width: 100%;
534   - }
535   -
536   - .device-top {
537   - padding: 10rpx 30rpx;
538   - background-color: #fff;
539   -
540   - .search {
541   - display: flex;
542   - justify-content: space-between;
543   - padding-bottom: 10rpx;
544   -
545   - .search-left {
546   - width: 580rpx;
547   - background-color: #f8f9fa;
548   - border-radius: 200rpx;
549   - }
550   -
551   - .search-right {
552   - display: flex;
553   - align-items: center;
554   -
555   - text {
556   - color: #333;
557   - font-size: 14px;
558   - }
559   -
560   - image {
561   - width: 40rpx;
562   - height: 40rpx;
563   - }
564   - }
565   - }
566   - }
567   -</style>
  1 +<template>
  2 + <view class="alarm-page" :class="pageDisableScroll">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <!-- 告警头部 -->
  6 + <header-search @openOrg="openOrg" @openSearchDialog="openSearchDialog" :total="alarmTotal"
  7 + :totalText="totalText">
  8 + <!-- 不能写在封装组件里传placeholder,mp-wenxin端编译要报错 -->
  9 + <u--input prefixIcon="search" placeholder="请输入告警设备" shape="circle" @change="inputChanged">
  10 + </u--input>
  11 + </header-search>
  12 + <!-- 告警分页 -->
  13 + <mescroll-body ref="mescrollRef" :up="upOption" @init="mescrollInit" :down="downOption" @down="downCallback"
  14 + @up="upCallback">
  15 + <alarm-item :list="list" @openAlertDetail="openAlertDetail"></alarm-item>
  16 + <mescroll-empty v-if="!list.length" />
  17 + </mescroll-body>
  18 + <view style="height: 20rpx"></view>
  19 + <!-- 告警筛选-->
  20 + <alarm-popup ref="alarmPopupRef" :show="show" @close="close" @queryCondition="getQueryCondition"></alarm-popup>
  21 + <f-tabbar></f-tabbar>
  22 + </view>
  23 +</template>
  24 +
  25 +<script>
  26 + import fTabbar from '@/components/module/f-tabbar/f-tabbar';
  27 + import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
  28 + import api from '@/api/index.js'
  29 + import alarmItem from './components/alarm-item.vue'
  30 + import alarmPopup from './components/alarm-popup.vue'
  31 + import {
  32 + usePageScrollTo,
  33 + useNavigateTo
  34 + } from '@/plugins/utils.js'
  35 +
  36 +
  37 + export default {
  38 + mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
  39 + components: {
  40 + fTabbar,
  41 + alarmItem,
  42 + alarmPopup
  43 + },
  44 + data() {
  45 + return {
  46 + totalText: '告警数:',
  47 + page: {
  48 + num: 0,
  49 + size: 10
  50 + },
  51 + downOption: {
  52 + auto: false //是否在初始化后,自动执行downCallback; 默认true
  53 + },
  54 + upOption: {
  55 + auto: false // 不自动加载
  56 + },
  57 + show: false,
  58 + list: [],
  59 + alarmTotal: 0,
  60 + queryCondition: {
  61 + searchAlarmText: '',
  62 + },
  63 + conditions: {},
  64 + ordId: '',
  65 + };
  66 + },
  67 + onShow() {
  68 + if (getApp().getBindNot()) {
  69 + return
  70 + }
  71 + if (this.ordId) {
  72 + this.loadData(1, {
  73 + organizationId: this.ordId
  74 + });
  75 + this.conditions = {
  76 + organizationId: this.ordId
  77 + }
  78 + }
  79 + },
  80 + onHide() {
  81 + this.ordId = ''
  82 + },
  83 + onLoad(e) {
  84 + if (!e.type) {
  85 + this.loadData(1);
  86 + } else {
  87 + let params = JSON.parse(e.type);
  88 + let status = null
  89 + if (Array.isArray(params)) {
  90 + status = params.join(',');
  91 + } else {
  92 + status = params;
  93 + }
  94 + this.conditions = {
  95 + status
  96 + }
  97 + this.loadData(1, {
  98 + status
  99 + });
  100 + }
  101 + // 隐藏原生的tabbar
  102 + uni.hideTabBar();
  103 + if (getApp().getBindNot()) {
  104 + return
  105 + }
  106 + },
  107 + computed: {
  108 + pageDisableScroll() {
  109 + return this.show ? 'pop-no-scroll' : ''
  110 + }
  111 + },
  112 + methods: {
  113 + inputChanged(e) {
  114 + this.resetQuery();
  115 + this.topBack();
  116 + this.queryCondition.searchAlarmText = e;
  117 + this.page.num = 1;
  118 + this.conditions = {
  119 + deviceName: e
  120 + }
  121 + this.loadData(1, {
  122 + deviceName: e
  123 + });
  124 + },
  125 + getQueryCondition(value) {
  126 + this.loadData(1, value);
  127 + this.conditions = value
  128 + this.close()
  129 + },
  130 + resetQuery() {
  131 + this.queryCondition.searchAlarmText = '';
  132 + this.page.num = 1;
  133 + this.$nextTick(() => {
  134 + this.$refs.alarmPopupRef.resetQuery()
  135 + })
  136 + this.conditions = {}
  137 + },
  138 + topBack() {
  139 + usePageScrollTo(0, 10)
  140 + },
  141 + //下拉刷新
  142 + downCallback() {
  143 + this.list.length = 0;
  144 + this.page.num = 1;
  145 + this.resetQuery();
  146 + this.loadData(this.page.num);
  147 + },
  148 + //上拉加载
  149 + upCallback() {
  150 + const condition = Object.values(this.conditions)
  151 + if (condition.length === 0) {
  152 + this.page.num += 1;
  153 + this.loadData(this.page.num);
  154 + } else if (condition.filter(Boolean).length > 0) {
  155 + this.page.num += 1;
  156 + this.loadData(this.page.num, this.conditions);
  157 + } else {
  158 + this.page.num += 1;
  159 + this.loadData(this.page.num);
  160 + }
  161 + },
  162 + async loadData(page, param) {
  163 + let that = this;
  164 + let params = {
  165 + page,
  166 + pageSize: 10,
  167 + ...param
  168 + };
  169 + const res = await api.alarmApi.getAlarmApi({
  170 + params,
  171 + custom: {
  172 + load: false
  173 + }
  174 + })
  175 + if (!res) return
  176 + uni.stopPullDownRefresh();
  177 + that.mescroll.endByPage(res.items.length, res.total); //必传参数(当前页的数据个数, 总页数)
  178 + that.alarmTotal = res.total;
  179 + if (page == 1) {
  180 + that.list = res.items;
  181 + } else {
  182 + that.list = that.list.concat(res.items);
  183 + }
  184 + },
  185 + close() {
  186 + this.show = false;
  187 + },
  188 + openSearchDialog() {
  189 + this.show = true;
  190 + this.$nextTick(() => {
  191 + this.resetQuery();
  192 + })
  193 + },
  194 + openOrg() {
  195 + useNavigateTo('/pages/organization/organization')
  196 + },
  197 + //跳转告警详情
  198 + openAlertDetail(e) {
  199 + useNavigateTo('/alarm-subpackage/alarm-detail/alarm-detail?data=', e)
  200 + },
  201 + }
  202 + };
  203 +</script>
  204 +
  205 +<style lang="scss" scoped>
  206 + @import './static/alarm.scss';
  207 +
  208 + /deep/ .u-button--primary {
  209 + background-color: #377dff !important;
  210 + border-color: #377dff !important;
  211 + }
  212 +
  213 + /deep/ .u-button--info {
  214 + background-color: #e3e3e5 !important;
  215 + border-color: #e3e3e5 !important;
  216 + }
  217 +
  218 + /deep/ .u-cell__right-icon-wrap {
  219 + margin-top: -55rpx !important;
  220 + }
  221 +
  222 + /deep/ .uni-calendar--fixed {
  223 + bottom: 172rpx !important;
  224 + }
  225 +
  226 + .pop-no-scroll {
  227 + overflow: hidden;
  228 + position: fixed;
  229 + height: 100%;
  230 + width: 100%;
  231 + }
  232 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <view class="alarm-list">
  3 + <view @click="$emit('openAlertDetail',item)" class="list-item" v-for="(item, index) in list" :key="index">
  4 + <view class="u-flex item">
  5 + <view class="item-text text-clip">
  6 + <text class="text-bold">{{ item.deviceName == null ? '暂无数据' : item.deviceName }}</text>
  7 + </view>
  8 + <view class="item-text text-clip">
  9 + <text class="text-muted">{{ item.details == null ? '暂无数据' : formatDetailText(item.details) }}</text>
  10 + </view>
  11 + <view class="item-text">
  12 + <text class="text-muted">
  13 + 告警状态:{{item.status | setAlarmStatus(alarmStatus)}}
  14 + </text>
  15 + </view>
  16 + <view class="item-text">
  17 + <text class="text-secondary">{{ item.createdTime }}</text>
  18 + </view>
  19 + </view>
  20 + <view class="item">
  21 + <view class="u-flex item-right">
  22 + <image class="right-image" :src="bindImageUrl(item.severity)"></image>
  23 + <view class="right-text">
  24 + <text class="text-no-color" :style="[setAlarmSeverityColor(item.severity,alarmSeverity)]">
  25 + {{item.severity | setAlarmSeverity(alarmSeverity)}}
  26 + </text>
  27 + </view>
  28 + </view>
  29 + </view>
  30 + </view>
  31 + </view>
  32 +</template>
  33 +
  34 +<script>
  35 + import {
  36 + alarmSeverity,
  37 + alarmStatus
  38 + } from '../config/data.js';
  39 +
  40 + export default {
  41 + props: {
  42 + list: {
  43 + type: Array,
  44 + default: []
  45 + }
  46 + },
  47 + data() {
  48 + return {
  49 + alarmSeverity,
  50 + alarmStatus
  51 + }
  52 + },
  53 + filters: {
  54 + setAlarmStatus(value, list) {
  55 + return list.find(item => item.value === value).label
  56 + },
  57 + setAlarmSeverity(value, list) {
  58 + return list.find(item => item.value === value).label
  59 + }
  60 + },
  61 + methods: {
  62 + setAlarmSeverityColor(value, list) {
  63 + return {
  64 + color: list.find(item => item.value === value).color
  65 + }
  66 + },
  67 + bindImageUrl(e) {
  68 + return this.alarmSeverity.find(item => item.value === e).icon
  69 + },
  70 + formatDetailText(e) {
  71 + const keys = Object.keys(e)
  72 + if(!keys) return
  73 + const values = keys.reduce((acc, curr) => {
  74 + acc.push(`${!e[curr].key?'暂无数据':e[curr].key}:${!e[curr].realValue?'暂无数据':e[curr].realValue}`)
  75 + return acc
  76 + }, [])
  77 + return values.join(',')
  78 + }
  79 + }
  80 + }
  81 +</script>
  82 +
  83 +<style lang="scss" scoped>
  84 + .alarm-list {
  85 + display: flex;
  86 + flex-direction: column;
  87 + padding-left: 18rpx;
  88 + justify-content: flex-start;
  89 +
  90 + .list-item {
  91 + width: 713rpx;
  92 + height: 233rpx;
  93 + background-color: #fff;
  94 + margin-top: 24rpx;
  95 + display: flex;
  96 + flex-direction: row;
  97 + border-radius: 10px;
  98 + justify-content: space-between;
  99 +
  100 + .item {
  101 + justify-content: flex-start;
  102 + flex-direction: column;
  103 + align-items: center;
  104 + height: 211rpx;
  105 + margin-top: 8rpx;
  106 + margin-left: 37rpx;
  107 +
  108 + .item-text {
  109 + width: 400rpx;
  110 + text-align: left;
  111 + margin-top: 13rpx;
  112 + line-height: 40rpx;
  113 +
  114 + .text {
  115 + color: #666666;
  116 + font-size: 15px;
  117 + }
  118 +
  119 + .text-three {
  120 + color: #333333;
  121 + font-size: 15px;
  122 + }
  123 +
  124 + .text-nine {
  125 + color: #999999;
  126 + font-size: 15px;
  127 + }
  128 + }
  129 +
  130 + .item-right {
  131 + flex-direction: row;
  132 + margin-top: -3rpx;
  133 + margin-right: 25rpx;
  134 +
  135 + .right-image {
  136 + width: 30rpx;
  137 + height: 30rpx;
  138 + margin-top: 20rpx;
  139 + margin-right: 5rpx;
  140 + }
  141 +
  142 + .right-text {
  143 + color: #333333;
  144 + font-size: 13px;
  145 + margin-left: 5rpx;
  146 + margin-top: 20rpx;
  147 + }
  148 + }
  149 + }
  150 + }
  151 + }
  152 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <u-popup @close="$emit('close')" closeable bgColor="transparent" :overlay="true" :show="show" mode="bottom">
  3 + <view class="popup-page">
  4 + <view class="popup-text"><text class="text">告警筛选</text></view>
  5 + <view class="popup-alarm-page u-flex">
  6 + <view>
  7 + <query-item ref="queryItemAlarmStatusRef" :leftText="leftAlarmStatusText" :queryStatus="alertStatus"
  8 + @currentClick="getAlarmStatus"></query-item>
  9 + <query-item ref="queryDeviceTypeStatusRef" :leftText="leftDeviceTypeText" :queryStatus="deviceType"
  10 + @currentClick="getDeviceType"></query-item>
  11 + <query-item ref="queryItemAlarmLevelRef" :leftText="leftAlarmLevelText" :queryStatus="alertLevel"
  12 + @currentClick="getAlarmLevel"></query-item>
  13 + <query-item ref="queryItemSelectTimeRef" :leftText="leftSelectTimeText" :queryStatus="timeArea"
  14 + @currentClick="getSelectTime"></query-item>
  15 + <view class="select-date">
  16 + <view class="home-text-muted">选择日期</view>
  17 + <view class="datetime-picker">
  18 + <uni-datetime-picker return-type="timestamp" v-model="range" type="datetimerange"
  19 + rangeSeparator="至" />
  20 + </view>
  21 + </view>
  22 + <view class="h-30"></view>
  23 + <view class="u-flex bottom-button">
  24 + <view class="button-item">
  25 + <u-button @click="resetQuery" type="info" shape="circle" text="重置">
  26 + </u-button>
  27 + </view>
  28 + <view class="button-item" style="margin-left: 46rpx">
  29 + <u-button @click="confirmQuery" type="primary" shape="circle" text="确认">
  30 + </u-button>
  31 + </view>
  32 + </view>
  33 + <view style="height: 90rpx;"></view>
  34 + </view>
  35 + </view>
  36 + </view>
  37 + </u-popup>
  38 +</template>
  39 +
  40 +<script>
  41 + import {
  42 + alertStatus,
  43 + deviceType,
  44 + alertLevel,
  45 + timeArea
  46 + } from '../config/data.js';
  47 + import queryItem from './query-item.vue'
  48 +
  49 + export default {
  50 + components: {
  51 + queryItem
  52 + },
  53 + props: {
  54 + show: Boolean
  55 + },
  56 + data() {
  57 + return {
  58 + totalText: '告警数:',
  59 + leftAlarmStatusText: '告警状态',
  60 + leftDeviceTypeText: '设备类型',
  61 + leftAlarmLevelText: '告警等级',
  62 + leftSelectTimeText: '选择时间',
  63 + range: [],
  64 + alertStatus,
  65 + deviceType,
  66 + alertLevel,
  67 + timeArea,
  68 + queryCondition: {
  69 + status: '',
  70 + deviceType: '',
  71 + severity: '',
  72 + startTime: 0,
  73 + endTime: 0,
  74 + },
  75 +
  76 + };
  77 + },
  78 + computed: {
  79 + hignLightColor() {
  80 + return `background: 'rgba(55, 125, 255, 0.05)', border: '1rpx solid rgba(55, 125, 255, 0.3)'`
  81 + },
  82 + unHighlightColor() {
  83 + return `background: '#F6F6F6'`
  84 + }
  85 + },
  86 + methods: {
  87 + getAlarmStatus(e) {
  88 + this.queryCondition.status = e.value;
  89 + },
  90 + getDeviceType(e) {
  91 + this.queryCondition.deviceType = e.value;
  92 + },
  93 + getAlarmLevel(e) {
  94 + this.queryCondition.severity = e.value;
  95 + },
  96 + getSelectTime(e, i) {
  97 + const curTime = new Date();
  98 + const getStartTs = curTime.getTime();
  99 + const calcDate = new Date(curTime.setMinutes(curTime.getMinutes() - e.value));
  100 + const getEndTs = calcDate.getTime();
  101 + this.queryCondition.startTime = getEndTs
  102 + this.queryCondition.endTime = getStartTs
  103 + },
  104 + confirmQuery() {
  105 + if (Array.isArray(this.range) && this.range.length > 0) {
  106 + this.queryCondition.startTime = this.range[0]
  107 + this.queryCondition.endTime = this.range[1]
  108 + }
  109 + this.$emit('queryCondition', this.queryCondition)
  110 + },
  111 + resetQuery() {
  112 + for (let i in this.queryCondition) Reflect.set(this.queryCondition, i, '')
  113 + this.range = []
  114 + this.$nextTick(() => {
  115 + this.$refs.queryItemAlarmStatusRef.reset()
  116 + this.$refs.queryDeviceTypeStatusRef.reset()
  117 + this.$refs.queryItemAlarmLevelRef.reset()
  118 + this.$refs.queryItemSelectTimeRef.reset()
  119 + })
  120 + },
  121 + }
  122 + };
  123 +</script>
  124 +
  125 +
  126 +<style lang="scss" scoped>
  127 + .popup-page {
  128 + height: 1100rpx;
  129 + background: #ffffff;
  130 + border-radius: 20rpx;
  131 + overflow-y: scroll;
  132 + overflow-x: hidden;
  133 +
  134 + .popup-text {
  135 + text-align: center;
  136 + position: relative;
  137 + top: 68rpx;
  138 + margin-top: -40rpx;
  139 +
  140 + .text {
  141 + font-size: 16px;
  142 + color: #333333;
  143 + }
  144 + }
  145 +
  146 + .popup-alarm-page {
  147 + margin-top: 97rpx;
  148 + margin-left: 98rpx;
  149 + flex-direction: column;
  150 + justify-content: space-between;
  151 +
  152 + .select-date {
  153 + display: flex;
  154 + flex-direction: column;
  155 + justify-content: space-between;
  156 + }
  157 +
  158 + .datetime-picker {
  159 + width: 640rpx;
  160 + margin-left: 5rpx;
  161 + margin-right: 70rpx;
  162 + margin-top: 35rpx;
  163 + }
  164 +
  165 + .bottom-button {
  166 + position: fixed;
  167 + bottom: 10rpx;
  168 + z-index: 9999;
  169 + flex-direction: row;
  170 + margin-top: 128rpx;
  171 + margin-left: 10rpx;
  172 +
  173 + .button-item {
  174 + width: 300rpx
  175 + }
  176 + }
  177 + }
  178 + }
  179 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <view>
  3 + <view class="popup-alarm-text"><text class="text">{{leftText}}</text></view>
  4 + <view class="u-flex popup-alarm-child">
  5 + <view v-for="(item, index) in queryStatus" :key="index" class="alarm-text" @click="currentClick(item,index)"
  6 + :style="[index == currentIndex ? { hignLightColor } : { unHighlightColor }]">
  7 + <text :class="[index == currentIndex ? 'select-text' : 'un-select-text']"
  8 + class="text">{{ item.name }}</text>
  9 + </view>
  10 + </view>
  11 + <view style="height:180rpx"></view>
  12 + </view>
  13 +</template>
  14 +
  15 +<script>
  16 + export default {
  17 + props: {
  18 + leftText: String,
  19 + queryStatus: Array
  20 + },
  21 + data() {
  22 + return {
  23 + currentIndex: 0,
  24 + }
  25 + },
  26 + computed: {
  27 + hignLightColor() {
  28 + return `background: 'rgba(55, 125, 255, 0.05)', border: '1rpx solid rgba(55, 125, 255, 0.3)'`
  29 + },
  30 + unHighlightColor() {
  31 + return `background: '#F6F6F6'`
  32 + }
  33 + },
  34 + methods: {
  35 + currentClick(item, index) {
  36 + this.currentIndex = index
  37 + this.$emit('currentClick', item, index)
  38 + },
  39 + reset() {
  40 + this.currentIndex = 0
  41 + }
  42 + }
  43 + }
  44 +</script>
  45 +
  46 +<style lang="scss" scoped>
  47 + .popup-alarm-text {
  48 + width: 750rpx;
  49 + margin-left: 14rpx;
  50 +
  51 + .text {
  52 + color: #333333;
  53 + font-size: 14px;
  54 + }
  55 + }
  56 +
  57 + .popup-alarm-child {
  58 + margin-top: 15rpx;
  59 + width: 750rpx;
  60 + height: 60rpx;
  61 + flex-wrap: wrap;
  62 + margin-left: -10rpx;
  63 +
  64 + .alarm-text {
  65 + margin: 25rpx;
  66 + line-height: 50rpx;
  67 + text-align: center;
  68 + width: 180rpx;
  69 + height: 60rpx;
  70 + background-color: #f6f6f6;
  71 + border-radius: 32px;
  72 +
  73 + .text {
  74 + color: #333333;
  75 + font-size: 13px;
  76 + }
  77 + }
  78 + }
  79 +</style>
\ No newline at end of file
... ...
pages/alarm/config/data.js renamed from pages/alarm/static/data.js
... ... @@ -72,7 +72,7 @@ const alertLevel = [{
72 72 },
73 73 {
74 74 index: 2,
75   - name: '危险',
  75 + name: '紧急',
76 76 value: 'CRITICAL',
77 77 bgColor: '#F6F6F6',
78 78 textColor: '#F6F6F6'
... ... @@ -143,9 +143,141 @@ const timeArea = [{
143 143 textColor: '#F6F6F6'
144 144 }
145 145 ]
  146 +
  147 +const alarmSeverity = [{
  148 + label: '紧急',
  149 + value: 'CRITICAL',
  150 + color: '#DE4437',
  151 + icon: '/static/danger.png',
  152 + },
  153 + {
  154 + label: '重要',
  155 + value: 'MAJOR',
  156 + color: '#DE7337',
  157 + icon: '/static/major.png',
  158 + },
  159 + {
  160 + label: '次要',
  161 + value: 'MINOR',
  162 + color: '#FFC107',
  163 + icon: '/static/secondary.png',
  164 + },
  165 + {
  166 + label: '警告',
  167 + value: 'WARNING',
  168 + color: '#DE4437',
  169 + icon: '/static/danger.png',
  170 + },
  171 + {
  172 + label: '不确定',
  173 + value: 'INDETERMINATE',
  174 + color: '#00C9A7',
  175 + icon: '/static/noshue.png',
  176 + },
  177 +]
  178 +
  179 +const alarmStatus = [{
  180 + label: '清除未确认',
  181 + value: 'CLEARED_UNACK'
  182 + },
  183 + {
  184 + label: '激活未确认',
  185 + value: 'ACTIVE_UNACK'
  186 + },
  187 + {
  188 + label: '清除已确认',
  189 + value: 'CLEARED_ACK'
  190 + },
  191 + {
  192 + label: '激活已确认',
  193 + value: 'ACTIVE_ACK'
  194 + },
  195 +]
  196 +
  197 +const operationNumberOrDate = [{
  198 + label: '等于',
  199 + value: 'EQUAL',
  200 + symbol: '='
  201 + },
  202 + {
  203 + label: '不等于',
  204 + value: 'NOT_EQUAL',
  205 + symbol: '!='
  206 + },
  207 + {
  208 + label: '小于',
  209 + value: 'LESS',
  210 + symbol: '<'
  211 + },
  212 + {
  213 + label: '小于等于',
  214 + value: 'LESS_OR_EQUAL',
  215 + symbol: '<='
  216 + },
  217 + {
  218 + label: '大于',
  219 + value: 'GREATER',
  220 + symbol: '>'
  221 + },
  222 + {
  223 + label: '大于等于',
  224 + value: 'GREATER_OR_EQUAL',
  225 + symbol: '>='
  226 + },
  227 +];
  228 +
  229 +const operationString = [{
  230 + label: '等于',
  231 + value: 'EQUAL',
  232 + symbol: '='
  233 + },
  234 + {
  235 + label: '不等于',
  236 + value: 'NOT_EQUAL',
  237 + symbol: '!='
  238 + },
  239 + {
  240 + label: '开始于',
  241 + value: 'STARTS_WITH',
  242 + symbol: '开始于'
  243 + },
  244 + {
  245 + label: '结束于',
  246 + value: 'ENDS_WITH',
  247 + symbol: '结束于'
  248 + },
  249 + {
  250 + label: '包含',
  251 + value: 'CONTAINS',
  252 + symbol: '包含'
  253 + },
  254 + {
  255 + label: '不包含',
  256 + value: 'NOT_CONTAINS',
  257 + symbol: '不包含'
  258 + },
  259 +];
  260 +
  261 +const operationBoolean = [{
  262 + label: '等于',
  263 + value: 'EQUAL',
  264 + symbol: '='
  265 + },
  266 + {
  267 + label: '不等于',
  268 + value: 'NOT_EQUAL',
  269 + symbol: '!='
  270 + },
  271 +];
  272 +
146 273 export {
147 274 alertStatus,
148 275 deviceType,
149 276 alertLevel,
150   - timeArea
151   -}
  277 + timeArea,
  278 + alarmSeverity,
  279 + alarmStatus,
  280 + operationNumberOrDate,
  281 + operationString,
  282 + operationBoolean
  283 +}
\ No newline at end of file
... ...
1   -.alert-page {
  1 +.alarm-page {
2 2 background: #f8f9fa;
3 3 .device-top {
4 4 padding: 10rpx 30rpx;
... ... @@ -109,129 +109,8 @@
109 109 }
110 110 }
111 111
112   -.device-list {
113   - display: flex;
114   - flex-direction: column;
115   - padding-left: 18rpx;
116   - justify-content: flex-start;
117   -
118   - .list-item {
119   - width: 713rpx;
120   - height: 233rpx;
121   - background-color: #fff;
122   - margin-top: 24rpx;
123   - display: flex;
124   - flex-direction: row;
125   - border-radius: 10px;
126   - justify-content: space-between;
127   -
128   - .item {
129   - justify-content: flex-start;
130   - flex-direction: column;
131   - align-items: center;
132   - height: 211rpx;
133   - margin-top: 8rpx;
134   - margin-left: 37rpx;
135   -
136   - .item-text {
137   - width: 400rpx;
138   - text-align: left;
139   - margin-top: 13rpx;
140   - line-height: 40rpx;
141   -
142   - .text {
143   - color: #666666;
144   - font-size: 15px;
145   - }
146   -
147   - .text-three {
148   - color: #333333;
149   - font-size: 15px;
150   - }
151 112
152   - .text-nine {
153   - color: #999999;
154   - font-size: 15px;
155   - }
156   - }
157 113
158   - .item-right {
159   - flex-direction: row;
160   - margin-top: -3rpx;
161   - margin-right: 25rpx;
162   -
163   - .right-image {
164   - width: 30rpx;
165   - height: 30rpx;
166   - margin-top: 20rpx;
167   - margin-right: 5rpx;
168   - }
169   -
170   - .right-text {
171   - color: #333333;
172   - font-size: 13px;
173   - margin-left: 5rpx;
174   - margin-top: 20rpx;
175   - }
176   - }
177   - }
178   - }
179   -}
180   -
181   -.popup-page {
182   - height: 1100rpx;
183   - background: #ffffff;
184   - border-radius: 20rpx;
185   - overflow-y: scroll;
186   - overflow-x: hidden;
187   -
188   - .popup-text {
189   - text-align: center;
190   - position: relative;
191   - top: 68rpx;
192   - margin-top: -40rpx;
193   -
194   - .text {
195   - font-size: 16px;
196   - color: #333333;
197   - }
198   - }
199   -
200   - .popup-alarm-page {
201   - margin-top: 97rpx;
202   - margin-left: 98rpx;
203   - flex-direction: column;
204   - justify-content: space-between;
205   - .popup-alarm-text {
206   - width: 750rpx;
207   - margin-left: 14rpx;
208   - .text {
209   - color: #333333;
210   - font-size: 14px;
211   - }
212   - }
213   - .popup-alarm-child {
214   - margin-top: 15rpx;
215   - width: 750rpx;
216   - height: 60rpx;
217   - flex-wrap: wrap;
218   - margin-left: -10rpx;
219   - .alarm-text {
220   - margin: 25rpx;
221   - line-height: 50rpx;
222   - text-align: center;
223   - width: 180rpx;
224   - height: 60rpx;
225   - background-color: #f6f6f6;
226   - border-radius: 32px;
227   - .text {
228   - color: #333333;
229   - font-size: 13px;
230   - }
231   - }
232   - }
233   - }
234   -}
235 114
236 115 .u-form {
237 116 width: 618rpx !important;
... ...
  1 +<template>
  2 + <view class="device-list">
  3 + <view @click="$emit('openDeviceDetail',item.id, item.alarmStatus, item.lastOnlineTime, item.tbDeviceId)"
  4 + class="list-item" v-for="item in list" :key="item.id">
  5 + <view class="u-flex item">
  6 + <view class="item-text text-clip">
  7 + <view>
  8 + <text class="text-span-bold">{{ item.alias ? item.alias : item.name }}</text>
  9 + </view>
  10 + </view>
  11 + <view class="item-text text-clip">
  12 + <view class="text-container">
  13 + 设备编号:
  14 + <text class="text-span">{{ item.sn }}</text>
  15 + </view>
  16 + </view>
  17 + <view class="item-text text-clip">
  18 + <view class="text-container">
  19 + 所属组织:
  20 + <text class="text-span">{{ item.organizationDTO.name }}</text>
  21 + </view>
  22 + </view>
  23 + </view>
  24 + <view class="item right-item">
  25 + <view class="u-flex" style="margin-top: -6rpx">
  26 + <image class="right-image" :src="formatRightImage(item.deviceState)" />
  27 + <view>
  28 + <text class="right-text" :style="{ color:formatColor(item.deviceState) }">
  29 + {{ formatText(item.deviceState) }}
  30 + </text>
  31 + </view>
  32 + </view>
  33 + </view>
  34 + </view>
  35 + </view>
  36 +</template>
  37 +
  38 +<script>
  39 + export default {
  40 + props: {
  41 + list: {
  42 + type: Array,
  43 + default: []
  44 + }
  45 + },
  46 + methods: {
  47 + formatRightImage(deviceState) {
  48 + return deviceState === 'ONLINE' ?
  49 + '/static/online.png' :
  50 + deviceState === 'INACTIVE' ?
  51 + '/static/unonline.png' :
  52 + '/static/baojing.png'
  53 + },
  54 + formatText(deviceState) {
  55 + return deviceState === 'ONLINE' ? '在线' : deviceState === 'INACTIVE' ? '待激活' : '离线'
  56 + },
  57 + formatColor(deviceState) {
  58 + return deviceState === 'ONLINE' ? '#377DFF' : deviceState === 'INACTIVE' ? '#666666' : '#DE4437'
  59 + }
  60 + }
  61 + }
  62 +</script>
  63 +
  64 +<style lang="scss" scoped>
  65 + .device-list {
  66 + display: flex;
  67 + flex-direction: column;
  68 + padding-left: 20rpx;
  69 +
  70 + .list-item {
  71 + width: 713rpx;
  72 + height: 200rpx;
  73 + background-color: #fff;
  74 + margin-top: 24rpx;
  75 + display: flex;
  76 + border-radius: 10px;
  77 + justify-content: space-between;
  78 +
  79 + .item {
  80 + margin: 30rpx;
  81 + flex-direction: column;
  82 + justify-content: space-between;
  83 +
  84 + .item-text {
  85 + width: 450rpx;
  86 +
  87 + .text-container {
  88 + display: flex;
  89 +
  90 + .text-span {
  91 + color: #666;
  92 + font-size: 14px;
  93 + display: flex;
  94 + margin-left: 20rpx;
  95 + }
  96 + }
  97 +
  98 +
  99 +
  100 + .text-span-bold {
  101 + color: #333;
  102 + font-size: 15px;
  103 + font-weight: bold;
  104 + }
  105 + }
  106 + }
  107 +
  108 + .right-item {
  109 + .right-image {
  110 + width: 30rpx;
  111 + height: 30rpx;
  112 + margin-top: 5rpx;
  113 + margin-right: 5rpx;
  114 + }
  115 +
  116 + .right-text {
  117 + color: #377dff;
  118 + font-size: 13px;
  119 + margin-left: 5rpx;
  120 + margin-top: 20rpx;
  121 + }
  122 + }
  123 + }
  124 + }
  125 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <u-popup @close="$emit('close')" closeable bgColor="#fff" :show="show" mode="bottom" :round="20"
  3 + @touchmove.stop.prevent="disabledScroll">
  4 + <view class="filter" @touchmove.stop.prevent="disabledScroll">
  5 + <view class="filter-title"><text>筛选条件</text></view>
  6 + <query-item :filterList="deviceStatus" title="设备状态"
  7 + @clickTag="currentIndex => handleClickTag(currentIndex, deviceStatus)"></query-item>
  8 + <query-item :filterList="alarmStatus" title="告警状态"
  9 + @clickTag="currentIndex => handleClickTag(currentIndex, alarmStatus)"></query-item>
  10 + <query-item :filterList="typeStatus" title="设备类型"
  11 + @clickTag="currentIndex => handleClickTag(currentIndex, typeStatus)"></query-item>
  12 + <view class="button-group">
  13 + <view>
  14 + <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="重置"
  15 + @click="resetFilter"></u-button>
  16 + </view>
  17 + <view>
  18 + <u-button color="#3388ff" shape="circle" text="确认" @click="confirmFilter"></u-button>
  19 + </view>
  20 + </view>
  21 + </view>
  22 + </u-popup>
  23 +</template>
  24 +
  25 +<script>
  26 + import queryItem from './query-item.vue'
  27 + import {
  28 + deviceStatus,
  29 + alarmStatus,
  30 + typeStatus
  31 + } from '../config/data.js'
  32 +
  33 + export default {
  34 + components: {
  35 + queryItem
  36 + },
  37 + props: {
  38 + show: Boolean
  39 + },
  40 + data() {
  41 + return {
  42 + deviceStatus,
  43 + alarmStatus,
  44 + typeStatus
  45 + }
  46 + },
  47 + methods: {
  48 + disabledScroll() {
  49 + return;
  50 + },
  51 + handleClickTag(currentIndex, list) {
  52 + list.map((item, index) => {
  53 + item.checked = index === currentIndex;
  54 + });
  55 + },
  56 + resetFilter() {
  57 + const {deviceStatus,alarmStatus,typeStatus} = this;
  58 + [deviceStatus, alarmStatus, typeStatus].forEach(item => item.map((item, index) => (item.checked = index ===0)));
  59 + },
  60 + confirmFilter() {
  61 + const deviceState = this.deviceStatus.find(item => item.checked);
  62 + const alarmStatus = this.alarmStatus.find(item => item.checked);
  63 + const deviceType = this.typeStatus.find(item => item.checked);
  64 + this.$emit('queryCondition', {
  65 + deviceState: deviceState.type ? deviceState.type : undefined,
  66 + deviceType: deviceType.type ? deviceType.type : undefined,
  67 + alarmStatus: alarmStatus.type === 0 || alarmStatus.type === 1 ? alarmStatus.type : undefined
  68 + })
  69 + },
  70 + }
  71 + }
  72 +</script>
  73 +
  74 +<style lang="scss" scoped>
  75 + .filter {
  76 + padding: 0 30rpx;
  77 +
  78 + .filter-title {
  79 + text-align: center;
  80 + margin-top: 14px;
  81 + font-size: 16px;
  82 + font-weight: 700;
  83 + }
  84 +
  85 + .button-group {
  86 + display: flex;
  87 + margin-top: 40rpx;
  88 + justify-content: space-between;
  89 +
  90 + view {
  91 + width: 330rpx;
  92 + }
  93 + }
  94 + }
  95 +</style>
\ No newline at end of file
... ...
pages/device/components/query-item.vue renamed from pages/device/FilterItem.vue
1   -<template>
2   - <view class="filter-item">
3   - <view class="filter-title">
4   - <text>{{ title }}</text>
5   - </view>
6   - <view class="filter-list">
7   - <view v-for="(item, index) in filterList" :key="index" @click="radioClick(index)" :class="['tag-item', { checked: item.checked, 'mr-30': (index + 1) % 3 !== 0 }]">
8   - {{ item.name }}
9   - </view>
10   - </view>
11   - </view>
12   -</template>
13   -
14   -<script>
15   -export default {
16   - props: {
17   - title: {
18   - type: String,
19   - default: ''
20   - },
21   - filterList: {
22   - type: Array,
23   - default: () => []
24   - }
25   - },
26   - methods: {
27   - radioClick(currentIndex) {
28   - this.$emit('clickTag', currentIndex);
29   - }
30   - }
31   -};
32   -</script>
33   -
34   -<style lang="scss" scoped>
35   -.filter-item {
36   - margin-top: 40rpx;
37   - .filter-title {
38   - color: #333;
39   - font-size: 14px;
40   - font-weight: 700;
41   - }
42   - .filter-list {
43   - display: flex;
44   - flex-wrap: wrap;
45   - .tag-item {
46   - margin-top: 30rpx;
47   - width: 210rpx;
48   - height: 68rpx;
49   - border-radius: 32rpx;
50   - display: flex;
51   - justify-content: center;
52   - align-items: center;
53   - color: #333;
54   - font-size: 13px;
55   - border: 1px solid #fff;
56   - background-color: #f6f6f6;
57   - }
58   - .checked {
59   - border: 1px solid #377dff4d;
60   - background-color: #377dff0d;
61   - color: #377dffff;
62   - }
63   - .mr-30 {
64   - margin-right: 30rpx;
65   - }
66   - }
67   -}
68   -</style>
  1 +<template>
  2 + <view class="query-item">
  3 + <view class="query-title">
  4 + <text>{{ title }}</text>
  5 + </view>
  6 + <view class="query-list">
  7 + <view v-for="(item, index) in filterList" :key="index" @click="itemClick(index)"
  8 + :class="['tag-item', { checked: item.checked, 'mr-30': (index + 1) % 3 !== 0 }]">
  9 + {{ item.name }}
  10 + </view>
  11 + </view>
  12 + </view>
  13 +</template>
  14 +
  15 +<script>
  16 + export default {
  17 + props: {
  18 + title: {
  19 + type: String,
  20 + default: ''
  21 + },
  22 + filterList: {
  23 + type: Array,
  24 + default: () => []
  25 + }
  26 + },
  27 + methods: {
  28 + itemClick(currentIndex) {
  29 + this.$emit('clickTag', currentIndex);
  30 + }
  31 + }
  32 + };
  33 +</script>
  34 +
  35 +<style lang="scss" scoped>
  36 + .query-item {
  37 + margin-top: 40rpx;
  38 +
  39 + .query-title {
  40 + color: #333;
  41 + font-size: 14px;
  42 + font-weight: 700;
  43 + }
  44 +
  45 + .query-list {
  46 + display: flex;
  47 + flex-wrap: wrap;
  48 +
  49 + .tag-item {
  50 + margin-top: 30rpx;
  51 + width: 210rpx;
  52 + height: 68rpx;
  53 + border-radius: 32rpx;
  54 + display: flex;
  55 + justify-content: center;
  56 + align-items: center;
  57 + color: #333;
  58 + font-size: 13px;
  59 + border: 1px solid #fff;
  60 + background-color: #f6f6f6;
  61 + }
  62 +
  63 + .checked {
  64 + border: 1px solid #377dff4d;
  65 + background-color: #377dff0d;
  66 + color: #377dffff;
  67 + }
  68 +
  69 + .mr-30 {
  70 + margin-right: 30rpx;
  71 + }
  72 + }
  73 + }
  74 +</style>
\ No newline at end of file
... ...
  1 +const deviceStatus = [{
  2 + checked: true,
  3 + name: '全部',
  4 + type: ''
  5 + },
  6 + {
  7 + checked: false,
  8 + name: '在线',
  9 + type: 'ONLINE'
  10 + },
  11 + {
  12 + checked: false,
  13 + name: '离线',
  14 + type: 'OFFLINE'
  15 + },
  16 + {
  17 + checked: false,
  18 + name: '待激活',
  19 + type: 'INACTIVE'
  20 + }
  21 +]
  22 +const alarmStatus = [{
  23 + checked: true,
  24 + name: '全部',
  25 + type: ''
  26 + },
  27 + {
  28 + checked: false,
  29 + name: '告警',
  30 + type: 1
  31 + },
  32 + {
  33 + checked: false,
  34 + name: '正常',
  35 + type: 0
  36 + }
  37 +]
  38 +const typeStatus = [{
  39 + checked: true,
  40 + name: '全部',
  41 + type: ''
  42 + },
  43 +
  44 + {
  45 + checked: false,
  46 + name: '直连设备',
  47 + type: 'DIRECT_CONNECTION'
  48 + },
  49 + {
  50 + checked: false,
  51 + name: '网关设备',
  52 + type: 'GATEWAY'
  53 + },
  54 + {
  55 + checked: false,
  56 + name: '网关子设备',
  57 + type: 'SENSOR'
  58 + }
  59 +]
  60 +
  61 +export {
  62 + deviceStatus,
  63 + alarmStatus,
  64 + typeStatus
  65 +}
\ No newline at end of file
... ...
1 1 <template>
2 2 <view class="device-page">
3   - <u-sticky>
4   - <view class="device-top">
5   - <view class="search">
6   - <view>
7   - <view class="search-left">
8   - <u--input prefixIcon="search" placeholder="输入设备SN或名称搜索" shape="circle"
9   - @change="inputChanged"></u--input>
10   - </view>
11   - </view>
12   - <view @click="openSearchDialog" class="search-right">
13   - <text>筛选</text>
14   - <image src="../../static/shaixuan.png" />
15   - </view>
16   - </view>
17   - <u-line />
18   - <view class="org">
19   - <u-cell @click="openOrg" isLink title="组织关系" :border="false">
20   - <view slot="label" class="label" style="display: flex; align-items: center;margin-top: 20rpx;">
21   - <image src="../../static/org.png" style="width: 24rpx;height: 28rpx;"></image>
22   - <view style="margin-left: 10rpx; color: #666;">
23   - 设备数:
24   - <text style="margin-left: 20rpx;">{{ total }}</text>
25   - </view>
26   - </view>
27   - </u-cell>
28   - </view>
29   - </view>
30   - </u-sticky>
  3 + <!-- 设备头部 -->
  4 + <header-search @openOrg="openOrg" @openSearchDialog="openSearchDialog" :total="total" :totalText="totalText">
  5 + <!-- 不能写在封装组件里传placeholder,mp-wenxin端编译要报错 -->
  6 + <u--input prefixIcon="search" placeholder="请输入设备SN或名称搜索" shape="circle" @change="inputChanged">
  7 + </u--input>
  8 + </header-search>
  9 + <!-- 设备分页 -->
31 10 <mescroll-body ref="mescrollRef" @init="mescrollInit" :up="upOption" :down="downOption" @down="downCallback"
32 11 @up="upCallback">
33   - <view class="device-list">
34   - <view @click="openDeviceDetail(item.id, item.alarmStatus, item.lastOnlineTime, item.tbDeviceId)"
35   - class="list-item" v-for="item in list" :key="item.id">
36   - <view class="u-flex item" style="
37   - justify-content: flex-start;
38   - flex-direction: column;
39   - align-items: center;
40   - ">
41   - <view style="width: 450rpx; text-align: left">
42   - <view class="text-clip" style="width:450rpx">
43   - <text class=""
44   - style="color: #333; font-size: 15px;font-weight: bold;">{{ item.alias ? item.alias : item.name }}</text>
45   - </view>
46   -
47   - </view>
48   - <view style="width: 450rpx; text-align: left; margin-top: 10rpx">
49   - <view style="color: #666; font-size: 14px;display: flex;">
50   - 设备编号:
51   - <view style="margin-left:16rpx">{{ item.sn }}</view>
52   - </view>
53   - </view>
54   - <view style="width: 450rpx; text-align: left; margin-top: 10rpx">
55   - <view style="color: #666; font-size: 14px;display: flex;">
56   - 所属组织:
57   - <view style="margin-left:16rpx">{{ item.organizationDTO.name }}</view>
58   - </view>
59   - </view>
60   - </view>
61   - <view class="item">
62   - <view class="u-flex" style="margin-top: -6rpx">
63   - <image style="
64   - width: 30rpx;
65   - height: 30rpx;
66   - margin-top: 5rpx;
67   - margin-right: 5rpx;
68   - " :src="
69   - item.deviceState === 'ONLINE'
70   - ? '../../static/online.png'
71   - : item.deviceState === 'INACTIVE'
72   - ? '../../static/unonline.png'
73   - : '../../static/baojing.png'
74   - " />
75   -
76   - <view>
77   - <text style="
78   - color: #377dff;
79   - font-size: 13px;
80   - margin-left: 5rpx;
81   - margin-top: 20rpx;
82   - " :style="{ color: item.deviceState === 'ONLINE' ? '#377DFF' : item.deviceState === 'INACTIVE' ? '#666666' : '#DE4437' }">
83   - {{ item.deviceState === 'ONLINE' ? '在线' : item.deviceState === 'INACTIVE' ? '待激活' : '离线' }}
84   - </text>
85   - </view>
86   - </view>
87   - </view>
88   - </view>
89   - </view>
  12 + <device-item :list="list" @openDeviceDetail="openDeviceDetail"></device-item>
90 13 </mescroll-body>
  14 + <view style="height: 20rpx"></view>
91 15 <!-- 设备筛选 -->
92   - <u-popup @close="close" closeable bgColor="#fff" :show="show" mode="bottom" :round="20"
93   - @touchmove.stop.prevent="disabledScroll">
94   - <view class="filter" @touchmove.stop.prevent="disabledScroll">
95   - <view class="filter-title"><text>筛选条件</text></view>
96   - <FilterItem :filterList="deviceStatus" title="设备状态"
97   - @clickTag="currentIndex => handleClickTag(currentIndex, deviceStatus)"></FilterItem>
98   - <FilterItem :filterList="alarmStatus" title="告警状态"
99   - @clickTag="currentIndex => handleClickTag(currentIndex, alarmStatus)"></FilterItem>
100   - <FilterItem :filterList="typeStatus" title="设备类型"
101   - @clickTag="currentIndex => handleClickTag(currentIndex, typeStatus)"></FilterItem>
102   - <view class="button-group">
103   - <view>
104   - <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="重置"
105   - @click="resetFilter"></u-button>
106   - </view>
107   - <view>
108   - <u-button color="#3388ff" shape="circle" text="确认" @click="confirmFilter"></u-button>
109   - </view>
110   - </view>
111   - </view>
112   - </u-popup>
  16 + <device-popup ref="devicePopupRef" :show="show" @close="close"
  17 + @queryCondition="getQueryCondition"></device-popup>
113 18 <f-tabbar></f-tabbar>
114 19 </view>
115 20 </template>
116 21
117 22 <script>
118 23 import fTabbar from '@/components/module/f-tabbar/f-tabbar';
119   - import FilterItem from './FilterItem.vue';
120 24 import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
121   - import {
122   - debounce
123   - } from '@/plugins/throttle.js';
124 25 import api from '@/api/index.js'
  26 + import {
  27 + useNavigateTo
  28 + } from '@/plugins/utils.js'
  29 + import deviceItem from './components/device-item.vue'
  30 + import devicePopup from './components/device-popup.vue'
125 31
126 32 export default {
127 33 mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
128 34 components: {
129 35 fTabbar,
130   - FilterItem
  36 + deviceItem,
  37 + devicePopup
131 38 },
132 39 data() {
133 40 return {
  41 + totalText: '设备数',
134 42 downOption: {
135 43 auto: false //是否在初始化后,自动执行downCallback; 默认true
136 44 },
... ... @@ -139,143 +47,91 @@
139 47 auto: false // 不自动加载
140 48 },
141 49 show: false,
142   - deviceStatus: [{
143   - checked: true,
144   - name: '全部',
145   - type: ''
146   - },
147   - {
148   - checked: false,
149   - name: '在线',
150   - type: 'ONLINE'
151   - },
152   - {
153   - checked: false,
154   - name: '离线',
155   - type: 'OFFLINE'
156   - },
157   - {
158   - checked: false,
159   - name: '待激活',
160   - type: 'INACTIVE'
161   - }
162   - ],
163   - alarmStatus: [{
164   - checked: true,
165   - name: '全部',
166   - type: ''
167   - },
168   - {
169   - checked: false,
170   - name: '告警',
171   - type: 1
172   - },
173   - {
174   - checked: false,
175   - name: '正常',
176   - type: 0
177   - }
178   - ],
179   - typeStatus: [{
180   - checked: true,
181   - name: '全部',
182   - type: ''
183   - },
184   -
185   - {
186   - checked: false,
187   - name: '直连设备',
188   - type: 'DIRECT_CONNECTION'
189   - },
190   - {
191   - checked: false,
192   - name: '网关设备',
193   - type: 'GATEWAY'
194   - },
195   - {
196   - checked: false,
197   - name: '网关子设备',
198   - type: 'SENSOR'
199   - }
200   - ],
201 50 total: 0,
202 51 list: [],
203 52 page: {
204 53 num: 0,
205 54 size: 10
206 55 },
207   - deviceState: '',
208   - deviceName: ''
  56 + deviceName: '',
  57 + ordId: '',
  58 + conditions: {}
209 59 };
210 60 },
211   - async onLoad(options) {
  61 + onLoad(e) {
212 62 // 隐藏原生的tabbar
213 63 uni.hideTabBar();
214   - if(getApp().getBindNot()){
  64 + if (getApp().getBindNot()) {
215 65 return
216 66 }
217   - this.page.num = 1;
218   - const {
219   - deviceState
220   - } = options;
221   - this.deviceState = deviceState;
222   - if (deviceState) {
223   - this.deviceStatus.forEach(item => {
224   - item.type === deviceState ? (item.checked = true) : (item.checked = false);
225   - });
226   - await this.loadData(1, {
227   - deviceState
228   - });
  67 + if (!e.deviceState) {
  68 + this.loadData(1);
229 69 } else {
230   - await this.loadData(1);
231   - }
232   - if (!this.list.length) {
233   - this.mescroll.showEmpty();
  70 + let params = JSON.parse(e.deviceState);
  71 + this.conditions = {
  72 + deviceState: params
  73 + }
  74 + this.loadData(1, {
  75 + deviceState: params
  76 + });
234 77 }
235 78 },
236 79 onShow() {
237   - if(getApp().getBindNot()){
  80 + if (getApp().getBindNot()) {
238 81 return
239 82 }
240   - if (this.orgId) {
  83 + if (this.ordId) {
241 84 this.loadData(1, {
242   - organizationId: this.orgId
  85 + organizationId: this.ordId
243 86 });
  87 + this.conditions = {
  88 + organizationId: this.ordId
  89 + }
244 90 }
245 91 },
246 92 methods: {
247   - disabledScroll() {
248   - return;
  93 + inputChanged(e) {
  94 + this.page.num = 1;
  95 + this.deviceName = e;
  96 + this.conditions = {
  97 + deviceName: e
  98 + }
  99 + this.loadData(1, {
  100 + name: this.deviceName
  101 + });
  102 + },
  103 + resetQuery() {
  104 + this.$refs.devicePopupRef.resetFilter()
  105 + this.ordId = ''
  106 + this.deviceName = ''
  107 + this.conditions = {}
  108 + },
  109 + getQueryCondition(value) {
  110 + const condition = Object.values(value)
  111 + this.page.num = 1;
  112 + this.loadData(this.page.num, value);
  113 + this.conditions = value
  114 + this.close()
249 115 },
250   - /*下拉刷新的回调 */
251 116 downCallback() {
252   - this.deviceName = '';
253   - this.orgId = '';
254   - //联网加载数据
255 117 this.list = [];
256 118 this.page.num = 1;
257   - //联网加载数据
258   - this.resetFilter();
259 119 this.loadData(this.page.num);
  120 + this.resetQuery();
260 121 },
261   -
262   - /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
263 122 upCallback() {
264   - //联网加载数据
265   - this.page.num += 1;
266   - const deviceState = this.deviceStatus.find(item => item.checked);
267   - const alarmStatus = this.alarmStatus.find(item => item.checked);
268   - const deviceType = this.typeStatus.find(item => item.checked);
269   - this.loadData(this.page.num, {
270   - deviceState: deviceState.type ? deviceState.type : undefined,
271   - deviceType: deviceType.type ? deviceType.type : undefined,
272   - alarmStatus: alarmStatus.type === 0 || alarmStatus.type === 1 ? alarmStatus.type :
273   - undefined,
274   - name: this.deviceName == null ? null : this.deviceName,
275   - organizationId: this.orgId == null ? null : this.orgId
276   - });
  123 + const condition = Object.values(this.conditions)
  124 + if (condition.length === 0) {
  125 + this.page.num += 1;
  126 + this.loadData(this.page.num);
  127 + } else if (condition.filter(Boolean).length > 0) {
  128 + this.page.num += 1;
  129 + this.loadData(this.page.num, this.conditions);
  130 + } else {
  131 + this.page.num += 1;
  132 + this.loadData(this.page.num);
  133 + }
277 134 },
278   -
279 135 //获取设备
280 136 async loadData(pageNo, params = {}) {
281 137 try {
... ... @@ -307,54 +163,22 @@
307 163 }
308 164 },
309 165 openOrg() {
310   - uni.navigateTo({
311   - url: './org/org'
312   - });
  166 + useNavigateTo('/pages/organization/organization')
313 167 },
314 168 close() {
315 169 this.show = false;
316 170 },
317 171 openSearchDialog() {
318 172 this.show = true;
  173 + this.$nextTick(() => {
  174 + this.resetQuery();
  175 + })
319 176 },
320 177 openDeviceDetail(id, alarmStatus, lastOnlineTime, tbDeviceId) {
321 178 uni.navigateTo({
322   - url: `/deviceSubPage/deviceDetailPage/deviceDetail?id=${id}&alarmStatus=${alarmStatus}&lastOnlineTime=${lastOnlineTime}&tbDeviceId=${tbDeviceId}`
  179 + url: `/device-subpackage/device-detail/device-detail?id=${id}&alarmStatus=${alarmStatus}&lastOnlineTime=${lastOnlineTime}&tbDeviceId=${tbDeviceId}`
323 180 });
324 181 },
325   - handleClickTag(currentIndex, list) {
326   - list.map((item, index) => {
327   - item.checked = index === currentIndex;
328   - });
329   - },
330   - resetFilter() {
331   - const {
332   - deviceStatus,
333   - alarmStatus,
334   - typeStatus
335   - } = this;
336   - [deviceStatus, alarmStatus, typeStatus].forEach(item => item.map((item, index) => (item.checked = index ===
337   - 0)));
338   - },
339   - confirmFilter() {
340   - const deviceState = this.deviceStatus.find(item => item.checked);
341   - const alarmStatus = this.alarmStatus.find(item => item.checked);
342   - const deviceType = this.typeStatus.find(item => item.checked);
343   - this.loadData(1, {
344   - deviceState: deviceState.type ? deviceState.type : undefined,
345   - deviceType: deviceType.type ? deviceType.type : undefined,
346   - alarmStatus: alarmStatus.type === 0 || alarmStatus.type === 1 ? alarmStatus.type :
347   - undefined
348   - });
349   - this.show = false;
350   - },
351   - inputChanged(e) {
352   - this.page.num = 1;
353   - this.deviceName = e;
354   - this.loadData(1, {
355   - name: this.deviceName
356   - });
357   - }
358 182 }
359 183 };
360 184 </script>
... ... @@ -396,45 +220,4 @@
396 220 }
397 221 }
398 222 }
399   -
400   - .device-list {
401   - display: flex;
402   - flex-direction: column;
403   - padding-left: 20rpx;
404   -
405   - .list-item {
406   - width: 713rpx;
407   - height: 200rpx;
408   - background-color: #fff;
409   - margin-top: 24rpx;
410   - display: flex;
411   - border-radius: 10px;
412   - justify-content: space-between;
413   -
414   - .item {
415   - margin: 30rpx;
416   - }
417   - }
418   - }
419   -
420   - .filter {
421   - padding: 0 30rpx;
422   -
423   - .filter-title {
424   - text-align: center;
425   - margin-top: 14px;
426   - font-size: 16px;
427   - font-weight: 700;
428   - }
429   -
430   - .button-group {
431   - display: flex;
432   - margin-top: 40rpx;
433   - justify-content: space-between;
434   -
435   - view {
436   - width: 330rpx;
437   - }
438   - }
439   - }
440   -</style>
  223 +</style>
\ No newline at end of file
... ...
1   -<template>
2   - <view class="org-content">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <view>
6   - <luyj-tree @sendValue="confirm" :is-check="isCheck" search-placeholder="请输入搜索内容" :search-if="true" v-slot:default="{ item }" :max="max" :trees="tree" :nodes="false">
7   - <view>
8   - <view class="content-item">
9   - <view class="word">{{ item.name }}</view>
10   - </view>
11   - </view>
12   - </luyj-tree>
13   - <mescroll-empty v-if="!tree.length" />
14   - </view>
15   - </view>
16   -</template>
17   -
18   -<script>
19   -import fTabbar from '@/components/module/f-tabbar/f-tabbar';
20   -import { transOrgFunc } from '@/config/common.js';
21   -export default {
22   - components: {
23   - fTabbar
24   - },
25   - data() {
26   - return {
27   - isCheck: true, // 是否可选
28   - tree: [],
29   - max: 5,
30   - id: ''
31   - };
32   - },
33   - onLoad(e) {
34   - // 隐藏原生的tabbar
35   - uni.hideTabBar();
36   - this.loadData();
37   - },
38   - methods: {
39   - loadData() {
40   - uni.$u.http
41   - .get('/yt/organization/me/list')
42   - .then(res => {
43   - if (res) {
44   - const list = transOrgFunc(res);
45   - this.tree = list;
46   - }
47   - })
48   - .catch(e => {
49   - // uni.$u.toast(e.data.message);
50   - });
51   - },
52   - confirm(val) {
53   - this.id = val[0].id;
54   - let pages = getCurrentPages(); //获取所有页面栈实例列表
55   - let nowPage = pages[pages.length - 1]; //当前页页面实例
56   - let prevPage = pages[pages.length - 2]; //上一页页面实例
57   - prevPage.$vm.orgId = this.id;
58   - uni.navigateBack({
59   - delta: 1
60   - });
61   - }
62   - }
63   -};
64   -</script>
65   -
66   -<style lang="scss" scoped>
67   -.org-content {
68   - padding: 0 10rpx;
69   -}
70   -</style>
1   -<template>
2   - <view class="org-content">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <view>
6   - <luyj-tree @sendValue="confirm" :is-check="isCheck" search-placeholder="请输入搜索内容" :search-if="true" v-slot:default="{ item }" :max="max" :trees="tree">
7   - <view>
8   - <view class="content-item">
9   - <view class="word">{{ item.name }}</view>
10   - </view>
11   - </view>
12   - </luyj-tree>
13   - </view>
14   - </view>
15   -</template>
16   -
17   -<script>
18   -import { transOrgFunc } from '@/config/common.js';
19   -
20   -export default {
21   - data() {
22   - return {
23   - isCheck: true, // 是否可选
24   - tree: [],
25   - max: 5,
26   - id: ''
27   - };
28   - },
29   - onLoad(e) {
30   - // 隐藏原生的tabbar
31   - uni.hideTabBar();
32   - this.loadData();
33   - },
34   - methods: {
35   - loadData() {
36   - uni.$u.http
37   - .get('/yt/organization/me/list')
38   - .then(res => {
39   - if (res) {
40   - const list = transOrgFunc(res);
41   - this.tree = list;
42   - }
43   - })
44   - .catch(e => {});
45   - },
46   - confirm(val) {
47   - this.id = val[0].id;
48   - let pages = getCurrentPages(); //获取所有页面栈实例列表
49   - let nowPage = pages[pages.length - 1]; //当前页页面实例
50   - let prevPage = pages[pages.length - 2]; //上一页页面实例
51   - prevPage.$vm.ordId = this.id;
52   - uni.navigateBack({
53   - delta: 1
54   - });
55   - }
56   - }
57   -};
58   -</script>
59   -
60   -<style lang="scss" scoped>
61   -.org-content {
62   - padding: 0 10rpx;
63   -}
64   -</style>
pages/index/components/camera/camera.vue renamed from pages/index/camera/camera.vue
1   -<template>
2   - <view class="camera-page">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <view class="org-sty">
6   - <view @click="openOrg" class="org-item">
7   - <view class="u-flex org-contact"><text class="text">组织关系</text></view>
8   - <view @click="openOrg" class="u-flex org-device">
9   - <image class="device-image" src="../../../static/org.png"></image>
10   - <text class="device-text">摄像头数:{{ cameraTotal }}</text>
11   - </view>
12   - </view>
13   - <view @click="openOrg" class="org-item">
14   - <image class="image" src="../../../static/arrow-right.png"></image>
15   - </view>
16   - </view>
17   - <view style="height: 150rpx;"></view>
18   - <!-- 自带分页组件 -->
19   - <mescroll-body ref="mescrollRef" :up="upOption" @init="mescrollInit" :down="downOption" @down="downCallback"
20   - @up="upCallback">
21   - <view class="camera-container">
22   - <view class="container-item">
23   - <view v-for="(item, index) in list" :key="item.id" class="item">
24   - <video :data-id="item.id" :data-accessMode="item.accessMode" :key="item.id" preload="none"
25   - :id="'video' + item.id" class="video" :src="item.videoUrl || commonVideoUrl" controls
26   - :title="item.name" x5-video-player-type="h5" x5-video-orientation="portraint" show-mute-btn
27   - :poster="item.avatar" @play="playVideo"></video>
28   - <view style="width:300rpx" class="bottom-text text-clip">
29   - <text class="text">{{ item.name }}</text>
30   - </view>
31   - </view>
32   - </view>
33   - </view>
34   - <mescroll-empty v-if="!list.length" />
35   - </mescroll-body>
36   - <!-- 自带分页组件 -->
37   - <view style="height: 60rpx;"></view>
38   - </view>
39   -</template>
40   -
41   -<script>
42   - import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
43   - import api from '@/api/index.js'
44   -
45   - export default {
46   - mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
47   - data() {
48   - return {
49   - page: {
50   - num: 0,
51   - size: 10
52   - },
53   - downOption: {
54   - auto: true //是否在初始化后,自动执行downCallback; 默认true
55   - },
56   - upOption: {
57   - auto: false // 不自动加载
58   - },
59   - current: 0,
60   - cameraTotal: 0,
61   - list: [],
62   - ordId: '',
63   - commonVideoUrl: 'http://playertest.longtailvideo.com/adaptive/bipbop/gear4/prog_index.m3u8'
64   - };
65   - },
66   - onShow() {
67   - if (this.ordId == '') {} else {
68   - this.loadData(1, this.ordId);
69   - }
70   - },
71   - onHide() {
72   - this.ordId = '';
73   - this.loadData(1, null);
74   - },
75   - onLoad() {
76   - // 隐藏原生的tabbar
77   - uni.hideTabBar();
78   - },
79   - methods: {
80   - /*下拉刷新的回调 */
81   - downCallback() {
82   - //联网加载数据
83   - this.page.num = 1;
84   - this.loadData(1);
85   - },
86   - /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
87   - upCallback() {
88   - //联网加载数据
89   - this.page.num += 1;
90   - this.loadData(this.page.num);
91   - },
92   - async loadData(pageNo, organizationV) {
93   - let httpData = {
94   - page: pageNo,
95   - pageSize: 10,
96   - organizationId: organizationV
97   - };
98   - const res = await api.homeApi.getCameraApi({
99   - params: httpData,
100   - custom: {
101   - load: false
102   - }
103   - })
104   - if (res) {
105   - uni.stopPullDownRefresh();
106   - this.mescroll.endByPage(res.items.length, res.total);
107   - this.cameraTotal = res.total;
108   - if (pageNo == 1) {
109   - this.list = res.items;
110   - } else {
111   - this.list = this.list.concat(res.items);
112   - }
113   - }
114   - },
115   - hideImageUrl(item, index) {
116   - this.current = index;
117   - },
118   - async playVideo(e) {
119   - const {
120   - currentTarget: {
121   - dataset: {
122   - accessmode,
123   - id
124   - }
125   - } = {}
126   - } = e
127   - const isExistVideoUrl = this.list.find(item => item.id == id).videoUrl
128   - if (accessmode === 1 && !isExistVideoUrl) {
129   - const res = api.homeApi.byCameraIdGetDetailApi(id)
130   - if (res) {
131   - const {
132   - data: {
133   - url
134   - } = {}
135   - } = res
136   - const index = this.list.findIndex(item => item.id === id)
137   - if (~index && url) {
138   - this.list.splice(index, 1, {
139   - ...this.list[index],
140   - videoUrl: url
141   - })
142   - this.$nextTick(() => {
143   - let currentId = 'video' + id;
144   - const videoContext = uni.createVideoContext(currentId, this);
145   - videoContext.play()
146   - })
147   - }
148   - }
149   - }
150   - /**
151   - * 点击全屏播放当前视频,暂停其余视频
152   - * 兼容APP和MP端
153   - */
154   - let currentId = 'video' + id;
155   - this.videoContent = uni.createVideoContext(currentId, this);
156   - this.videoContent.requestFullScreen();
157   - // 获取视频列表
158   - let trailer = this.list;
159   - trailer.forEach((item, index) => {
160   - if (item.videoUrl != null && item.videoUrl != '') {
161   - let temp = 'video' + item.id;
162   - if (temp != currentId) {
163   - //暂停不是当前的视频
164   - uni.createVideoContext(temp, this).pause();
165   - }
166   - }
167   - });
168   - },
169   - openOrg() {
170   - uni.navigateTo({
171   - url: './org/org'
172   - });
173   - }
174   - }
175   - };
176   -</script>
177   -
178   -<style lang="scss" scoped>
179   - @import '../static/camera.scss';
180   -</style>
  1 +<template>
  2 + <view class="camera-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <header-org @openOrg="openOrg" :total="cameraTotal" :imageSrc="imageSrc"></header-org>
  6 + <view style="height: 150rpx;"></view>
  7 + <!-- 自带分页组件 -->
  8 + <mescroll-body ref="mescrollRef" :up="upOption" @init="mescrollInit" :down="downOption" @down="downCallback"
  9 + @up="upCallback">
  10 + <view class="camera-container">
  11 + <view class="container-item">
  12 + <view v-for="(item, index) in list" :key="item.id" class="item">
  13 + <video :data-id="item.id" :data-accessMode="item.accessMode" :key="item.id" preload="none"
  14 + :id="'video' + item.id" class="video" :src="item.videoUrl" controls :title="item.name"
  15 + x5-video-player-type="h5" x5-video-orientation="portraint" show-mute-btn
  16 + :poster="item.avatar" @play="playVideo"></video>
  17 + <view class="bottom-text text-clip w-300">
  18 + <text class="text">{{ item.name }}</text>
  19 + </view>
  20 + </view>
  21 + </view>
  22 + </view>
  23 + <mescroll-empty v-if="!list.length" />
  24 + </mescroll-body>
  25 + <!-- 自带分页组件 -->
  26 + <view style="height: 60rpx;"></view>
  27 + </view>
  28 +</template>
  29 +
  30 +<script>
  31 + import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
  32 + import api from '@/api/index.js'
  33 + import {
  34 + useNavigateTo
  35 + } from '@/plugins/utils.js'
  36 + import headerOrg from '@/components/common/header-org.vue'
  37 +
  38 + export default {
  39 + mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
  40 + components:{
  41 + headerOrg
  42 + },
  43 + data() {
  44 + return {
  45 + imageSrc:'/static/org.png',
  46 + page: {
  47 + num: 0,
  48 + size: 10
  49 + },
  50 + downOption: {
  51 + auto: true //是否在初始化后,自动执行downCallback; 默认true
  52 + },
  53 + upOption: {
  54 + auto: false // 不自动加载
  55 + },
  56 + current: 0,
  57 + cameraTotal: 0,
  58 + list: [],
  59 + ordId: '',
  60 + };
  61 + },
  62 + onShow() {
  63 + if (this.ordId) {
  64 + this.loadData(1, this.ordId);
  65 + }
  66 + },
  67 + onHide() {
  68 + this.ordId = '';
  69 + },
  70 + onLoad() {
  71 + // 隐藏原生的tabbar
  72 + uni.hideTabBar();
  73 + },
  74 + methods: {
  75 + /*下拉刷新的回调 */
  76 + downCallback() {
  77 + //联网加载数据
  78 + this.page.num = 1;
  79 + this.loadData(1);
  80 + },
  81 + /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
  82 + upCallback() {
  83 + //联网加载数据
  84 + this.page.num += 1;
  85 + this.loadData(this.page.num);
  86 + },
  87 + async loadData(pageNo, organizationId) {
  88 + let httpData = {
  89 + page: pageNo,
  90 + pageSize: 10,
  91 + organizationId
  92 + };
  93 + const res = await api.homeApi.getCameraApi({
  94 + params: httpData,
  95 + custom: {
  96 + load: false
  97 + }
  98 + })
  99 + if (res) {
  100 + uni.stopPullDownRefresh();
  101 + this.mescroll.endByPage(res.items.length, res.total);
  102 + this.cameraTotal = res.total;
  103 + if (pageNo == 1) {
  104 + this.list = res.items;
  105 + } else {
  106 + this.list = this.list.concat(res.items);
  107 + }
  108 + }
  109 + },
  110 + //播放视频
  111 + async playVideo(e) {
  112 + const {currentTarget: {dataset: {accessmode,id}} = {}} = e
  113 + const isExistVideoUrl = this.list.find(item => item.id == id).videoUrl
  114 + if (accessmode === 1 && !isExistVideoUrl) {
  115 + const res = api.homeApi.byCameraIdGetDetailApi(id)
  116 + if (res) {
  117 + const {data: {url} = {}} = res
  118 + const index = this.list.findIndex(item => item.id === id)
  119 + if (~index && url) {
  120 + this.list.splice(index, 1, {
  121 + ...this.list[index],
  122 + videoUrl: url
  123 + })
  124 + this.$nextTick(() => {
  125 + let currentId = 'video' + id;
  126 + const videoContext = uni.createVideoContext(currentId, this);
  127 + videoContext.play()
  128 + })
  129 + }
  130 + }
  131 + }
  132 + /**
  133 + * 点击全屏播放当前视频,暂停其余视频
  134 + * 兼容APP和MP端
  135 + */
  136 + let currentId = 'video' + id;
  137 + this.videoContent = uni.createVideoContext(currentId, this);
  138 + this.videoContent.requestFullScreen();
  139 + // 获取视频列表
  140 + let trailer = this.list;
  141 + trailer.forEach((item, index) => {
  142 + if (item.videoUrl != null && item.videoUrl != '') {
  143 + let temp = 'video' + item.id;
  144 + if (temp != currentId) {
  145 + //暂停不是当前的视频
  146 + uni.createVideoContext(temp, this).pause();
  147 + }
  148 + }
  149 + });
  150 + },
  151 + openOrg() {
  152 + useNavigateTo('/pages/organization/organization')
  153 + }
  154 + }
  155 + };
  156 +</script>
  157 +
  158 +<style lang="scss" scoped>
  159 + @import '../../static/camera.scss';
  160 +</style>
\ No newline at end of file
... ...
pages/index/components/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   - defauleConfigImage: 'https://dev.thingskit.com',
15   - params: ''
16   - };
17   - },
18   - onLoad(e) {
19   - uni.setStorageSync('getConfiguration', {
20   - isConfiguration: true
21   - });
22   - if (e.configId !== null) {
23   - this.params = e.configurationHref;
24   - this.requestUrl(this.params);
25   - }
26   - // 隐藏原生的tabbar
27   - uni.hideTabBar();
28   - },
29   - onShow() {
30   - uni.setStorageSync('getConfiguration', {
31   - isConfiguration: true
32   - });
33   - },
34   - onUnload() {
35   - uni.setStorageSync('getConfiguration', {
36   - isConfiguration: false
37   - });
38   - uni.removeStorageSync('config');
39   - },
40   - methods: {
41   - // 网页加载成功时候触发此事件
42   - bindload(res) {
43   - console.log(res, res.detail);
44   - },
45   - // 网页加载失败的时候触发此事件
46   - binderror(err) {
47   - console.log(err, err.detail);
48   - },
49   - async requestUrl(e) {
50   - const httpData = {
51   - configurationId: e,
52   - lightbox: 1
53   - };
54   - const getUrl = await uni.$u.http.get('/', {
55   - params: httpData,
56   - custom: {
57   - load: false
58   - }
59   - });
60   - const pathUrl = uni.getStorageSync('config');
61   - const userInfo = uni.getStorageSync('userInfo')
62   - console.log(this.params)
63   - this.showConfiguration = this.params
64   - }
65   - }
66   - };
67   -</script>
68   -
69   -<style lang="scss" scoped>
70   - .content {
71   - background: #f8f9fa;
72   - min-height: 100vh;
73   - }
74   -</style>
  1 +<template>
  2 + <view class="configuation-detail-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <web-view :src="showConfiguration"></web-view>
  6 + </view>
  7 +</template>
  8 +
  9 +<script>
  10 + export default {
  11 + data() {
  12 + return {
  13 + showConfiguration: '',
  14 + defauleConfigImage: 'https://dev.thingskit.com',
  15 + params: ''
  16 + };
  17 + },
  18 + onLoad(e) {
  19 + uni.setStorageSync('getConfiguration', {
  20 + isConfiguration: true
  21 + });
  22 + if (e.configId !== null) {
  23 + this.params = e.configurationHref;
  24 + this.requestUrl(this.params);
  25 + }
  26 + // 隐藏原生的tabbar
  27 + uni.hideTabBar();
  28 + },
  29 + onShow() {
  30 + uni.setStorageSync('getConfiguration', {
  31 + isConfiguration: true
  32 + });
  33 + },
  34 + onUnload() {
  35 + uni.setStorageSync('getConfiguration', {
  36 + isConfiguration: false
  37 + });
  38 + uni.removeStorageSync('config');
  39 + },
  40 + methods: {
  41 + async requestUrl(e) {
  42 + const httpData = {
  43 + configurationId: e,
  44 + lightbox: 1
  45 + };
  46 + const getUrl = await uni.$u.http.get('/', {
  47 + params: httpData,
  48 + custom: {
  49 + load: false
  50 + }
  51 + });
  52 + const pathUrl = uni.getStorageSync('config');
  53 + const userInfo = uni.getStorageSync('userInfo')
  54 + this.showConfiguration = this.params
  55 + }
  56 + }
  57 + };
  58 +</script>
  59 +
  60 +<style lang="scss" scoped>
  61 + @import '../../static/configuration.scss';
  62 +</style>
\ No newline at end of file
... ...
pages/index/components/configuration/configuration.vue renamed from pages/index/configuration/configuration.vue
1   -<template>
2   - <view class="status-page">
3   - <view style="margin-left:15rpx;background-color: #f8f9fa;position:fixed;top:0rpx;z-index: 99999;">
4   - <view style="height:35rpx;background-color: #f8f9fa;"></view>
5   - <view class="u-flex search-top">
6   - <view class="search-main">
7   - <u--input @change="inputChanged" prefixIcon="search" placeholder="请输入组态名称" border="surround"
8   - shape="circle"></u--input>
9   - </view>
10   - </view>
11   - </view>
12   - <view style="height:35rpx"></view>
13   - <!-- 公共组件-每个页面必须引入 -->
14   - <public-module></public-module>
15   - <!-- 自带分页组件 -->
16   - <mescroll-body ref="mescrollRef" :up="upOption" @init="mescrollInit" :down="downOption" @down="downCallback"
17   - @up="upCallback">
18   - <view class="configuation-container">
19   - <view class="configuation-item">
20   - <view @click="openConfigDetail(item)" v-for="(item, index) in list" :key="index" class="item">
21   - <image class="image" :src="item.icon || defaultConfigImage"></image>
22   - <text class="name">{{ item.name }}</text>
23   - </view>
24   - </view>
25   - </view>
26   - <mescroll-empty v-if="!list.length" />
27   - </mescroll-body>
28   - <!-- 自带分页组件 -->
29   - <view style="height: 60rpx;"></view>
30   - </view>
31   -</template>
32   -
33   -<script>
34   - import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
35   - import api from '@/api/index.js'
36   - import {
37   - createScadaPageLink
38   - } from './help';
39   -
40   - export default {
41   - mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
42   - data() {
43   - return {
44   - defaultConfigImage: '../../../static/test.png',
45   - page: {
46   - num: 0,
47   - size: 10
48   - },
49   - downOption: {
50   - auto: true //是否在初始化后,自动执行downCallback; 默认true
51   - },
52   - upOption: {
53   - auto: false // 不自动加载
54   - },
55   - list: []
56   - };
57   - },
58   - onLoad() {
59   - // 隐藏原生的tabbar
60   - uni.hideTabBar();
61   - uni.setStorageSync('getConfiguration', {
62   - isConfiguration: false
63   - });
64   - },
65   - onUnload() {
66   - uni.setStorageSync('getConfiguration', {
67   - isConfiguration: false
68   - });
69   - uni.removeStorageSync('getConfiguration');
70   - },
71   - methods: {
72   - inputChanged(e) {
73   - this.page.num = 1;
74   - this.loadData(1, e);
75   - },
76   - openConfigDetail(e) {
77   - const href = createScadaPageLink(e)
78   - uni.navigateTo({
79   - url: 'configurationDetail?configurationHref=' + href
80   - });
81   - },
82   - /*下拉刷新的回调 */
83   - downCallback() {
84   - //联网加载数据
85   - this.page.num = 1;
86   - this.loadData(1);
87   - },
88   - /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
89   - upCallback() {
90   - //联网加载数据
91   - this.page.num += 1;
92   - this.loadData(this.page.num);
93   - },
94   - async loadData(pageNo, organizationV) {
95   - let httpData = {
96   - page: pageNo,
97   - pageSize: 10,
98   - name: organizationV,
99   - platform: 'phone'
100   - };
101   - const res = await api.homeApi.getConfigurationApi({
102   - params: httpData,
103   - custom: {
104   - load: false
105   - }
106   - })
107   - if (res) {
108   - uni.stopPullDownRefresh();
109   - this.mescroll.endByPage(res.items.length, res.total);
110   - this.cameraTotal = res.total;
111   - if (pageNo == 1) {
112   - this.list = res.items;
113   - } else {
114   - this.list = this.list.concat(res.items);
115   - }
116   - }
117   - }
118   - }
119   - };
120   -</script>
121   -
122   -<style lang="scss" scoped>
123   - @import '../static/configuration.scss';
  1 +<template>
  2 + <view class="configuation-page">
  3 + <view class="configuation-header">
  4 + <view class="header-gap"></view>
  5 + <view class="search-top">
  6 + <view class="search-main">
  7 + <u--input @change="inputChanged" prefixIcon="search" placeholder="请输入组态名称" border="surround"
  8 + shape="circle"></u--input>
  9 + </view>
  10 + </view>
  11 + </view>
  12 + <view style="height:35rpx"></view>
  13 + <!-- 公共组件-每个页面必须引入 -->
  14 + <public-module></public-module>
  15 + <!-- 自带分页组件 -->
  16 + <mescroll-body ref="mescrollRef" :up="upOption" @init="mescrollInit" :down="downOption" @down="downCallback"
  17 + @up="upCallback">
  18 + <view class="configuation-container">
  19 + <view class="configuation-item">
  20 + <view @click="openConfigDetail(item)" v-for="(item, index) in list" :key="index" class="item">
  21 + <image class="image" :src="item.icon || defaultConfigImage"></image>
  22 + <text class="name">{{ item.name }}</text>
  23 + </view>
  24 + </view>
  25 + </view>
  26 + <mescroll-empty v-if="!list.length" />
  27 + </mescroll-body>
  28 + <!-- 自带分页组件 -->
  29 + <view style="height: 60rpx;"></view>
  30 + </view>
  31 +</template>
  32 +
  33 +<script>
  34 + import MescrollMixin from '@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js';
  35 + import api from '@/api/index.js'
  36 + import {
  37 + createScadaPageLink
  38 + } from './help';
  39 +
  40 + export default {
  41 + mixins: [MescrollMixin], // 使用mixin (在main.js注册全局组件)
  42 + data() {
  43 + return {
  44 + defaultConfigImage: '../../../../static/test.png',
  45 + page: {
  46 + num: 0,
  47 + size: 10
  48 + },
  49 + downOption: {
  50 + auto: true //是否在初始化后,自动执行downCallback; 默认true
  51 + },
  52 + upOption: {
  53 + auto: false // 不自动加载
  54 + },
  55 + list: []
  56 + };
  57 + },
  58 + onLoad() {
  59 + // 隐藏原生的tabbar
  60 + uni.hideTabBar();
  61 + uni.setStorageSync('getConfiguration', {
  62 + isConfiguration: false
  63 + });
  64 + },
  65 + onUnload() {
  66 + uni.setStorageSync('getConfiguration', {
  67 + isConfiguration: false
  68 + });
  69 + uni.removeStorageSync('getConfiguration');
  70 + },
  71 + methods: {
  72 + inputChanged(e) {
  73 + this.page.num = 1;
  74 + this.loadData(1, e);
  75 + },
  76 + openConfigDetail(e) {
  77 + const href = createScadaPageLink(e)
  78 + uni.navigateTo({
  79 + url: 'configuration-detail?configurationHref=' + href
  80 + });
  81 + },
  82 + /*下拉刷新的回调 */
  83 + downCallback() {
  84 + //联网加载数据
  85 + this.page.num = 1;
  86 + this.loadData(1);
  87 + },
  88 + /*上拉加载的回调: 其中page.num:当前页 从1开始, page.size:每页数据条数,默认10 */
  89 + upCallback() {
  90 + //联网加载数据
  91 + this.page.num += 1;
  92 + this.loadData(this.page.num);
  93 + },
  94 + async loadData(pageNo, organizationV) {
  95 + let httpData = {
  96 + page: pageNo,
  97 + pageSize: 10,
  98 + name: organizationV,
  99 + platform: 'phone'
  100 + };
  101 + const res = await api.homeApi.getConfigurationApi({
  102 + params: httpData,
  103 + custom: {
  104 + load: false
  105 + }
  106 + })
  107 + if (res) {
  108 + uni.stopPullDownRefresh();
  109 + this.mescroll.endByPage(res.items.length, res.total);
  110 + this.cameraTotal = res.total;
  111 + if (pageNo == 1) {
  112 + this.list = res.items;
  113 + } else {
  114 + this.list = this.list.concat(res.items);
  115 + }
  116 + }
  117 + }
  118 + }
  119 + };
  120 +</script>
  121 +
  122 +<style lang="scss" scoped>
  123 + @import '../../static/configuration.scss';
124 124 </style>
\ No newline at end of file
... ...
pages/index/components/configuration/help.js renamed from pages/index/configuration/help.js
1   -import config from '../../../config/baseUrl.js'
2   -import {
3   - atob,
4   - btoa
5   -} from './weapp.atob.js'
6   -const getRandomString = () => Number(Math.random().toString().substring(2)).toString(36);
7   -
8   -export const ScadaModeEnum = {
9   - PRIVATE_VIEW: 'PRIVATE_VIEW',
10   - PUBLIC_VIEW: 'PUBLIC_VIEW',
11   -}
12   -
13   -export const encode = (record) => {
14   - let hash = JSON.stringify(record);
15   - const mixinString = getRandomString()
16   - .slice(0, 10)
17   - .padEnd(10, getRandomString())
18   - .split('')
19   - .map((item) => (Math.random() > 0.5 ? item.toUpperCase() : item))
20   - .join('');
21   - hash = btoa(hash);
22   - hash = hash.substring(0, 6) + mixinString + hash.substring(6);
23   - hash = btoa(hash);
24   - return hash;
25   -};
26   -
27   -
28   -export const createScadaPageLink = (
29   - record,
30   - mode = ScadaModeEnum.PRIVATE_VIEW,
31   - open = false
32   -) => {
33   - const userInfo = uni.getStorageSync('userInfo')
34   - const params = {
35   - configurationId: record?.id,
36   - organizationId: record?.organizationId,
37   - mode: record?.viewType === ScadaModeEnum.PRIVATE_VIEW ? 'lightbox' : 'share',
  1 +import config from '../../../../config/baseUrl.js'
  2 +import {
  3 + atob,
  4 + btoa
  5 +} from './weapp.atob.js'
  6 +const getRandomString = () => Number(Math.random().toString().substring(2)).toString(36);
  7 +
  8 +export const ScadaModeEnum = {
  9 + PRIVATE_VIEW: 'PRIVATE_VIEW',
  10 + PUBLIC_VIEW: 'PUBLIC_VIEW',
  11 +}
  12 +
  13 +export const encode = (record) => {
  14 + let hash = JSON.stringify(record);
  15 + const mixinString = getRandomString()
  16 + .slice(0, 10)
  17 + .padEnd(10, getRandomString())
  18 + .split('')
  19 + .map((item) => (Math.random() > 0.5 ? item.toUpperCase() : item))
  20 + .join('');
  21 + hash = btoa(hash);
  22 + hash = hash.substring(0, 6) + mixinString + hash.substring(6);
  23 + hash = btoa(hash);
  24 + return hash;
  25 +};
  26 +
  27 +
  28 +export const createScadaPageLink = (
  29 + record,
  30 + mode = ScadaModeEnum.PRIVATE_VIEW,
  31 + open = false
  32 +) => {
  33 + const userInfo = uni.getStorageSync('userInfo')
  34 + const params = {
  35 + configurationId: record?.id,
  36 + organizationId: record?.organizationId,
  37 + mode: record?.viewType === ScadaModeEnum.PRIVATE_VIEW ? 'lightbox' : 'share',
38 38 platform: record?.platform,
39   - userId: userInfo.userId
40   - };
41   -
42   - if (record?.viewType === ScadaModeEnum.PUBLIC_VIEW) {
43   - params.publicId = record.publicId;
44   - }
45   -
46   - const hash = encode(params);
47   -
48   - const href = `${config.baseDrawioUrl}#${hash}`
49   -
50   - return href
  39 + userId: userInfo.userId
  40 + };
  41 +
  42 + if (record?.viewType === ScadaModeEnum.PUBLIC_VIEW) {
  43 + params.publicId = record.publicId;
  44 + }
  45 +
  46 + const hash = encode(params);
  47 +
  48 + const href = `${config.baseDrawioUrl}#${hash}`
  49 +
  50 + return href
51 51 };
\ No newline at end of file
... ...
pages/index/components/configuration/weapp.atob.js renamed from pages/index/configuration/weapp.atob.js
1   -var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
2   -var b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
3   -export const btoa = function(string) {
4   - string = String(string);
5   - var bitmap, a, b, c, result = "",
6   - i = 0,
7   - rest = string.length % 3;
8   - for (; i < string.length;) {
9   - if ((a = string.charCodeAt(i++)) > 255 ||
10   - (b = string.charCodeAt(i++)) > 255 ||
11   - (c = string.charCodeAt(i++)) > 255)
12   - throw new TypeError(
13   - "Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range."
14   - );
15   - bitmap = (a << 16) | (b << 8) | c;
16   - result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63) +
17   - b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63);
18   - }
19   - return rest ? result.slice(0, rest - 3) + "===".substring(rest) : result;
20   -};
21   -
22   -export const atob = function(string) {
23   - string = String(string).replace(/[\t\n\f\r ]+/g, "");
24   - if (!b64re.test(string))
25   - throw new TypeError(
26   - "Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");
27   - string += "==".slice(2 - (string.length & 3));
28   - var bitmap, result = "",
29   - r1, r2, i = 0;
30   - for (; i < string.length;) {
31   - bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12 |
32   - (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++)));
33   - result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255) :
34   - r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255) :
35   - String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255);
36   - }
37   - return result;
38   -};
39   -
40   -function b64DecodeUnicode(str) {
41   - return decodeURIComponent(exports.weAtob(str).replace(/(.)/g, function(p) {
42   - var code = p.charCodeAt(0).toString(16).toUpperCase();
43   - if (code.length < 2) {
44   - code = "0" + code;
45   - }
46   - return "%" + code;
47   - }));
48   -}
49   -
50   -function base64_url_decode(str) {
51   - var output = str.replace(/-/g, "+").replace(/_/g, "/");
52   - switch (output.length % 4) {
53   - case 0:
54   - break;
55   - case 2:
56   - output += "==";
57   - break;
58   - case 3:
59   - output += "=";
60   - break;
61   - default:
62   - throw "Illegal base64url string!";
63   - }
64   - try {
65   - return b64DecodeUnicode(output);
66   - } catch (err) {
67   - return exports.weAtob(output);
68   - }
69   -}
70   -
71   -function weappJwtDecode(token, options) {
72   - if (typeof token !== "string") {
73   - throw ("Invalid token specified");
74   - }
75   - options = options || {};
76   - var pos = options.header === true ? 0 : 1;
77   - try {
78   - return JSON.parse(base64_url_decode(token.split(".")[pos]));
79   - } catch (e) {
80   - throw ("Invalid token specified: " + e.message);
81   - }
  1 +var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  2 +var b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
  3 +export const btoa = function(string) {
  4 + string = String(string);
  5 + var bitmap, a, b, c, result = "",
  6 + i = 0,
  7 + rest = string.length % 3;
  8 + for (; i < string.length;) {
  9 + if ((a = string.charCodeAt(i++)) > 255 ||
  10 + (b = string.charCodeAt(i++)) > 255 ||
  11 + (c = string.charCodeAt(i++)) > 255)
  12 + throw new TypeError(
  13 + "Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range."
  14 + );
  15 + bitmap = (a << 16) | (b << 8) | c;
  16 + result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63) +
  17 + b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63);
  18 + }
  19 + return rest ? result.slice(0, rest - 3) + "===".substring(rest) : result;
  20 +};
  21 +
  22 +export const atob = function(string) {
  23 + string = String(string).replace(/[\t\n\f\r ]+/g, "");
  24 + if (!b64re.test(string))
  25 + throw new TypeError(
  26 + "Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");
  27 + string += "==".slice(2 - (string.length & 3));
  28 + var bitmap, result = "",
  29 + r1, r2, i = 0;
  30 + for (; i < string.length;) {
  31 + bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12 |
  32 + (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++)));
  33 + result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255) :
  34 + r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255) :
  35 + String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255);
  36 + }
  37 + return result;
  38 +};
  39 +
  40 +function b64DecodeUnicode(str) {
  41 + return decodeURIComponent(exports.weAtob(str).replace(/(.)/g, function(p) {
  42 + var code = p.charCodeAt(0).toString(16).toUpperCase();
  43 + if (code.length < 2) {
  44 + code = "0" + code;
  45 + }
  46 + return "%" + code;
  47 + }));
  48 +}
  49 +
  50 +function base64_url_decode(str) {
  51 + var output = str.replace(/-/g, "+").replace(/_/g, "/");
  52 + switch (output.length % 4) {
  53 + case 0:
  54 + break;
  55 + case 2:
  56 + output += "==";
  57 + break;
  58 + case 3:
  59 + output += "=";
  60 + break;
  61 + default:
  62 + throw "Illegal base64url string!";
  63 + }
  64 + try {
  65 + return b64DecodeUnicode(output);
  66 + } catch (err) {
  67 + return exports.weAtob(output);
  68 + }
  69 +}
  70 +
  71 +function weappJwtDecode(token, options) {
  72 + if (typeof token !== "string") {
  73 + throw ("Invalid token specified");
  74 + }
  75 + options = options || {};
  76 + var pos = options.header === true ? 0 : 1;
  77 + try {
  78 + return JSON.parse(base64_url_decode(token.split(".")[pos]));
  79 + } catch (e) {
  80 + throw ("Invalid token specified: " + e.message);
  81 + }
82 82 }
\ No newline at end of file
... ...
  1 +const basicGridList = [{
  2 + event: 'openCamera',
  3 + icon: '/static/camer.png',
  4 + text: '摄像头管理'
  5 + },
  6 + {
  7 + event: 'openConfiguration',
  8 + icon: '/static/status.png',
  9 + text: '组态'
  10 + },
  11 +]
  12 +
  13 +const basicStatistics = [{
  14 + text: '设备统计',
  15 + key: 'device',
  16 + icon: '/static/device-total.png',
  17 + leftParam: 'ONLINE',
  18 + centerParam: 'OFFLINE',
  19 + rightParam: 'INACTIVE',
  20 + value: {
  21 + leftValue: 0,
  22 + centerValue: 0,
  23 + rightValue: 0
  24 + },
  25 + label: {
  26 + leftText: "在线",
  27 + centerText: "离线",
  28 + rightText: "待激活"
  29 + }
  30 + },
  31 + {
  32 + text: '告警统计',
  33 + key: 'alarm',
  34 + icon: '/static/alert.png',
  35 + leftParam: ['ACTIVE_ACK'],
  36 + centerParam: 'CLEARED_ACK',
  37 + rightParam: 'CLEARED_UNACK',
  38 + value: {
  39 + leftValue: 0,
  40 + centerValue: 0,
  41 + rightValue: 0
  42 + },
  43 + label: {
  44 + leftText: "未处理",
  45 + centerText: "已处理",
  46 + rightText: "误报"
  47 + }
  48 + },
  49 +]
  50 +
  51 +export {
  52 + basicGridList,
  53 + basicStatistics
  54 +}
\ No newline at end of file
... ...
1   -<template>
2   - <view class="index-page">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <view>
6   - <!-- 基础统计 -->
7   - <view class="basic-sty">
8   - <view class="basic-text"><text class="text text-bold">基础统计</text></view>
9   - <view class="basic">
10   - <view class="basic-item">
11   - <view class="item-child-top u-flex">
12   - <image class="item-image" src="../../static/device-total.png"></image>
13   - <text class="item-text home-text-muted">设备统计</text>
14   - </view>
15   - <view class="item-child-bottom u-flex">
16   - <view @click="navigatorDeviceStatus('ONLINE')" class="u-flex sigle-child">
17   - <view class="sigle-text">
18   - <text class="home-text-total">{{ deviceData.onLine }}</text>
19   - </view>
20   - <view class="sigle-value"><text class="home-text-total-bottom">在线</text></view>
21   - </view>
22   - <view @click="navigatorDeviceStatus('OFFLINE')" class="u-flex sigle-child">
23   - <view class="sigle-text">
24   - <text class="home-text-total">{{ deviceData.offLine }}</text>
25   - </view>
26   - <view class="sigle-value"><text class="home-text-total-bottom">离线</text></view>
27   - </view>
28   - <view @click="navigatorDeviceStatus('INACTIVE')" class="u-flex sigle-child">
29   - <view class="sigle-text">
30   - <text class="home-text-total">{{ deviceData.inActive }}</text>
31   - </view>
32   - <view class="sigle-value"><text class="home-text-total-bottom">待激活</text></view>
33   - </view>
34   - </view>
35   - </view>
36   - <view class="basic-item">
37   - <view class="item-child-top u-flex">
38   - <image class="item-image" src="../../static/alert.png"></image>
39   - <text class="item-text home-text-muted">告警统计</text>
40   - </view>
41   - <view class="item-child-bottom u-flex">
42   - <view @click="navigatorAlarmStatus(['ACTIVE_UNACK'])"
43   - class="u-flex sigle-child">
44   - <view class="sigle-text">
45   - <text class="home-text-total">{{ alertData.activedAlarm }}</text>
46   - </view>
47   - <view class="sigle-value"><text class="home-text-total-bottom">未处理</text></view>
48   - </view>
49   - <view @click="navigatorAlarmStatus('CLEARED_ACK')" class="u-flex sigle-child">
50   - <view class="sigle-text">
51   - <text class="home-text-total">{{ alertData.clearedAck }}</text>
52   - </view>
53   - <view class="sigle-value"><text class="home-text-total-bottom">已处理</text></view>
54   - </view>
55   - <view @click="navigatorAlarmStatus('CLEARED_UNACK')" class="u-flex sigle-child">
56   - <view class="sigle-text">
57   - <text class="home-text-total">{{ alertData.clearedUnack }}</text>
58   - </view>
59   - <view class="sigle-value"><text class="home-text-total-bottom">误报</text></view>
60   - </view>
61   - </view>
62   - </view>
63   - </view>
64   - </view>
65   - <!-- 基础统计 -->
66   - <!-- 四宫格CSS最新网格布局-->
67   - <view class="grid-container">
68   - <!-- <view class="grid-item" @click="showToastWip()">
69   - <view class="item-center">
70   - <view class="center"><image class="image" src="../../static/form.png"></image></view>
71   - <view class="center-text"><text class="text text-muted">维修工单</text></view>
72   - </view>
73   - </view> -->
74   - <view @click="openCamera" class="grid-item">
75   - <view class="item-center">
76   - <view class="center">
77   - <image class="image" src="../../static/camer.png"></image>
78   - </view>
79   - <view class="center-text"><text class="text text-muted" style="">摄像头管理</text></view>
80   - </view>
81   - </view>
82   - <!-- <view class="grid-item" @click="showToastWip()">
83   - <view class="item-center">
84   - <view class="center"><image class="image" src="../../static/device.png"></image></view>
85   - <view class="center-text"><text class="text text-muted">设备接入</text></view>
86   - </view>
87   - </view> -->
88   - <view @click="openOrgStatus" class="grid-item">
89   - <view class="item-center">
90   - <view class="center">
91   - <image class="image" src="../../static/status.png"></image>
92   - </view>
93   - <view class="center-text"><text class="text text-muted">组态</text></view>
94   - </view>
95   - </view>
96   - </view>
97   - <!-- 四宫格 -->
98   - </view>
99   - <f-tabbar></f-tabbar>
100   - </view>
101   -</template>
102   -
103   -<script>
104   - import fTabbar from '@/components/module/f-tabbar/f-tabbar';
105   - import {
106   - mapActions
107   - } from 'vuex';
  1 +<template>
  2 + <view class="index-page">
  3 + <!-- 公共组件-每个页面必须引入 -->
  4 + <public-module></public-module>
  5 + <view>
  6 + <!-- 基础统计 -->
  7 + <view class="basic-statistics">
  8 + <view class="basic-text"><text class="text text-bold">基础统计</text></view>
  9 + <view class="basic">
  10 + <view class="basic-item" v-for="(item,index) in basicStatistics" :key="index">
  11 + <view class="item-child-top u-flex">
  12 + <image class="item-image" :src="item.icon"></image>
  13 + <text class="item-text home-text-muted">{{item.text}}</text>
  14 + </view>
  15 + <view class="item-child-bottom u-flex">
  16 + <view @click="navigatorPage(item,item.leftParam)" class="u-flex sigle-child">
  17 + <view class="sigle-text">
  18 + <text class="home-text-total">{{item.value.leftValue}}</text>
  19 + </view>
  20 + <view class="sigle-value"><text
  21 + class="home-text-total-bottom">{{item.label.leftText}}</text></view>
  22 + </view>
  23 + <view @click="navigatorPage(item,item.centerParam)" class="u-flex sigle-child">
  24 + <view class="sigle-text">
  25 + <text class="home-text-total">{{item.value.centerValue}}</text>
  26 + </view>
  27 + <view class="sigle-value"><text
  28 + class="home-text-total-bottom">{{item.label.centerText}}</text></view>
  29 + </view>
  30 + <view @click="navigatorPage(item,item.rightParam)" class="u-flex sigle-child">
  31 + <view class="sigle-text">
  32 + <text class="home-text-total">{{item.value.rightValue}}</text>
  33 + </view>
  34 + <view class="sigle-value"><text
  35 + class="home-text-total-bottom">{{item.label.rightText}}</text></view>
  36 + </view>
  37 + </view>
  38 + </view>
  39 + </view>
  40 + </view>
  41 + <!-- 网格信息 -->
  42 + <view class="grid-container">
  43 + <view v-for="(item,index) in basicGridList" :key="index" @click="handleEvent(item.event)"
  44 + class="grid-item">
  45 + <view class="item-center">
  46 + <view class="center">
  47 + <image class="image" :src="item.icon"></image>
  48 + </view>
  49 + <view class="center-text"><text class="text text-muted" style="">{{item.text}}</text></view>
  50 + </view>
  51 + </view>
  52 + </view>
  53 + </view>
  54 + <f-tabbar></f-tabbar>
  55 + </view>
  56 +</template>
  57 +
  58 +<script>
  59 + import fTabbar from '@/components/module/f-tabbar/f-tabbar';
  60 + import {
  61 + mapActions
  62 + } from 'vuex';
108 63 import api from '@/api/index.js'
109   - import { useNavigateTo } from '@/plugins/utils.js'
110   -
111   - export default {
112   - components: {
113   - fTabbar
114   - },
115   - data() {
116   - return {
117   - deviceData: {
118   - onLine: 0,
119   - offLine: 0,
120   - inActive: 0
121   - },
122   - alertData: {
123   - activedAlarm: 0,
124   - clearedAck: 0,
125   - clearedUnack: 0
126   - }
127   - };
128   - },
129   - onLoad() {
130   - // 隐藏原生的tabbar
131   - uni.hideTabBar();
132   - if(getApp().getBindNot()){
133   - return
134   - }
135   - this.getDeviceTotalData();
136   - uni.setStorageSync('getConfiguration', {
137   - isConfiguration: false
138   - });
139   - uni.removeStorageSync('getConfiguration');
  64 + import {
  65 + useNavigateTo
  66 + } from '@/plugins/utils.js'
  67 + import {
  68 + basicGridList,
  69 + basicStatistics
  70 + } from './config/data.js'
  71 +
  72 + export default {
  73 + components: {
  74 + fTabbar
  75 + },
  76 + data() {
  77 + return {
  78 + basicGridList,
  79 + basicStatistics,
  80 + };
  81 + },
  82 + onLoad() {
  83 + // 隐藏原生的tabbar
  84 + uni.hideTabBar();
  85 + if (getApp().getBindNot()) {
  86 + return
  87 + }
  88 + this.getDeviceTotalData();
  89 + uni.setStorageSync('getConfiguration', {
  90 + isConfiguration: false
  91 + });
  92 + uni.removeStorageSync('getConfiguration');
140 93 },
141 94 onPullDownRefresh() {
142 95 this.getDeviceTotalData();
143   - setTimeout(function () {
  96 + setTimeout(function() {
144 97 uni.stopPullDownRefresh();
145 98 uni.$u.toast('下拉刷新成功...');
146   - }, 1000);
147   - },
148   - methods: {
149   - ...mapActions(['updateBadgeTotal']),
150   - async getDeviceTotalData() {
151   - const res = await api.homeApi.getHomeStatisticsApi()
  99 + }, 200);
  100 + },
  101 + methods: {
  102 + ...mapActions(['updateBadgeTotal']),
  103 + async getDeviceTotalData() {
  104 + const res = await api.homeApi.getHomeStatisticsApi()
152 105 if (res) {
153   - for(let i in this.deviceData) Reflect.set(this.deviceData,i,res.totalDevice[i])
154   - for(let i in this.alertData) Reflect.set(this.alertData,i,res.totalAlarm[i])
155   - //异步实时更新告警徽标数
156   - this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
157   - }
158   - },
159   - showToastWip() {
160   - uni.$u.toast('拼命开发中 ...');
161   - },
  106 + const { onLine,offLine,inActive } = res.totalDevice
  107 + const { activedAlarm,clearedAck,clearedUnack } = res.totalAlarm
  108 + this.basicStatistics.map(item=>{
  109 + const { key, value } = item
  110 + if(key === 'device'){
  111 + value.leftValue = onLine
  112 + value.centerValue =offLine
  113 + value.rightValue = inActive
  114 + }else{
  115 + value.leftValue = activedAlarm
  116 + value.centerValue =clearedAck
  117 + value.rightValue = clearedUnack
  118 + }
  119 + })
  120 + //异步实时更新告警徽标数
  121 + this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
  122 + }
  123 + },
162 124 openCamera() {
163   - useNavigateTo('camera/camera')
164   - },
165   - openOrgStatus() {
166   - useNavigateTo('configuration/configuration')
167   - },
168   - //告警状态查询
169   - navigatorAlarmStatus(e) {
170   - uni.reLaunch({
171   - url: '../alarm/alarm?type=' + JSON.stringify(e)
172   - });
173   - },
174   - //设备状态查询
175   - navigatorDeviceStatus(e) {
176   - uni.reLaunch({
177   - url: `../device/device?deviceState=${e}`
178   - });
179   - }
180   - }
181   - };
182   -</script>
183   -
184   -<style lang="scss" scoped>
185   - @import './static/index.scss';
186   -</style>
  125 + useNavigateTo('components/camera/camera')
  126 + },
  127 + openConfiguration() {
  128 + useNavigateTo('components/configuration/configuration')
  129 + },
  130 + handleEvent(event) {
  131 + if (event === 'openCamera') this.openCamera()
  132 + else {
  133 + this.openConfiguration()
  134 + }
  135 + },
  136 + navigatorPage(item, type) {
  137 + const {
  138 + text
  139 + } = item
  140 + if (text === '设备统计') this.navigatorDeviceStatus(type)
  141 + else {
  142 + this.navigatorAlarmStatus(type)
  143 + }
  144 + },
  145 + //告警状态查询
  146 + navigatorAlarmStatus(e) {
  147 + uni.reLaunch({
  148 + url: '../alarm/alarm?type=' + JSON.stringify(e)
  149 + });
  150 + },
  151 + //设备状态查询
  152 + navigatorDeviceStatus(e) {
  153 + uni.reLaunch({
  154 + url: '../device/device?deviceState=' + JSON.stringify(e)
  155 + });
  156 + }
  157 + }
  158 + };
  159 +</script>
  160 +
  161 +<style lang="scss" scoped>
  162 + @import './static/index.scss';
  163 +</style>
\ No newline at end of file
... ...
1 1 <template>
2   - <view class="container">
3   - <view class="container-box">
4   - <image :src="mpOwnConfig.logo"></image>
  2 + <view class="splash-container">
  3 + <view class="splash-container-box">
  4 + <image v-if="initConfig.logo" :src="initConfig.logo"></image>
  5 + <image v-else :src="staticLogo"></image>
5 6 <text class="splash-text-muted">连接世界 创造价值</text>
6 7 </view>
7 8 </view>
... ... @@ -12,12 +13,15 @@
12 13 mapState
13 14 } from 'vuex';
14 15 import api from '@/api/index.js'
  16 + import {
  17 + useReLaunch
  18 + } from '@/plugins/utils.js'
15 19
16 20 export default {
17 21 data() {
18 22 return {
19   - staticLogo: '../../static/logo.png',
20   - mpOwnConfig: {}
  23 + staticLogo: '/static/logo.png',
  24 + initConfig: {}
21 25 };
22 26 },
23 27 onLoad() {
... ... @@ -29,16 +33,12 @@
29 33 created() {
30 34 if (!this.userInfo.isToken) {
31 35 setTimeout(() => {
32   - uni.reLaunch({
33   - url: '/publicLoginSubPage/public/login'
34   - });
35   - }, 1500);
  36 + useReLaunch('/login-subpackage/public/login')
  37 + }, 1000);
36 38 } else {
37 39 setTimeout(() => {
38   - uni.reLaunch({
39   - url: '/pages/index/index'
40   - });
41   - }, 1500);
  40 + useReLaunch('/pages/index/index')
  41 + }, 1000);
42 42 }
43 43 },
44 44 computed: {
... ... @@ -47,7 +47,7 @@
47 47 methods: {
48 48 async getPlateForm() {
49 49 const res = await api.loginApi.getPlateCustomApi()
50   - this.mpOwnConfig = {
  50 + this.initConfig = {
51 51 bg: res.background,
52 52 logo: res.logo ? res.logo : this.staticLogo,
53 53 name: res.name
... ... @@ -57,22 +57,7 @@
57 57 };
58 58 </script>
59 59
60   -<style lang="scss">
61   - .container {
62   - width: 100%;
63   - height: 100%;
64   - margin-top: 300rpx;
65   -
66   - .container-box {
67   - display: flex;
68   - align-items: center;
69   - justify-content: space-between;
70   - flex-direction: column;
71 60
72   - image {
73   - width: 100rpx;
74   - height: 100rpx;
75   - }
76   - }
77   - }
78   -</style>
  61 +<style lang="scss" scoped>
  62 + @import './static/splash.scss';
  63 +</style>
\ No newline at end of file
... ...
... ... @@ -2,54 +2,6 @@
2 2 min-height: 100vh;
3 3 background-color: #f0f2f5;
4 4 }
5   -.org-sty {
6   - width: 750rpx;
7   - height: 150rpx;
8   - margin-top: 1rpx;
9   - background-color: #fff;
10   - display: flex;
11   - flex-direction: row;
12   - justify-content: space-between;
13   - position: fixed;
14   - z-index: 999999;
15   - top: -1rpx;
16   - .org-item {
17   - width: 350rpx;
18   - height: 200rpx;
19   - .org-contact {
20   - flex-direction: row;
21   - margin-top: 26rpx;
22   - margin-left: 15rpx;
23   - .text {
24   - color: #333333;
25   - font-size: 15px;
26   - margin-left: 14rpx;
27   - }
28   - }
29   - .org-device {
30   - margin-top: 23rpx;
31   - margin-left: 15rpx;
32   - flex-direction: row;
33   - .device-image {
34   - margin-left: 14rpx;
35   - width: 30rpx;
36   - height: 30rpx;
37   - }
38   - .device-text {
39   - margin-left: 10rpx;
40   - color: #666666;
41   - font-size: 12px;
42   - }
43   - }
44   - .image {
45   - width: 6px;
46   - height: 10px;
47   - float: right;
48   - margin-right: 34rpx;
49   - margin-top: 37rpx;
50   - }
51   - }
52   -}
53 5 .camera-item {
54 6 height: 200rpx;
55 7 border: 0.1px solid gray;
... ...
1   -.status-page {
  1 +.configuation-page {
2 2 background: #f8f9fa;
3 3 padding: 15rpx 15rpx;
4 4 min-height: 100vh;
5   - .search-top {
6   - justify-content: space-between;
7   - flex-direction: row;
8   - .search-main {
9   - width: 700rpx;
10   - margin-top: -34rpx;
  5 + .configuation-header{
  6 + margin-left:15rpx;
  7 + background-color: #f8f9fa;
  8 + position:fixed;
  9 + top:0rpx;
  10 + z-index: 99999;
  11 + .header-gap{
  12 + height:35rpx;
  13 + background-color: #f8f9fa;
  14 + }
  15 + .search-top {
  16 + justify-content: space-between;
  17 + flex-direction: row;
  18 + .search-main {
  19 + width: 700rpx;
  20 + margin-top: -34rpx;
  21 + }
11 22 }
12 23 }
  24 +
13 25 .configuation-container {
14 26 .configuation-item {
15 27 width: 750rpx;
... ... @@ -43,3 +55,8 @@
43 55 }
44 56 }
45 57 }
  58 +
  59 +.configuation-detail-page {
  60 + background: #f8f9fa;
  61 + min-height: 100vh;
  62 + }
\ No newline at end of file
... ...
... ... @@ -56,7 +56,7 @@
56 56 }
57 57 }
58 58 }
59   -.basic-sty {
  59 +.basic-statistics {
60 60 padding: 0 10rpx;
61 61 .basic-text {
62 62 .text {
... ...
  1 +.splash-container {
  2 + width: 100%;
  3 + height: 100%;
  4 + margin-top: 300rpx;
  5 +
  6 + .splash-container-box {
  7 + display: flex;
  8 + align-items: center;
  9 + justify-content: space-between;
  10 + flex-direction: column;
  11 +
  12 + image {
  13 + width: 100rpx;
  14 + height: 100rpx;
  15 + }
  16 + }
  17 + }
\ No newline at end of file
... ...
... ... @@ -17,6 +17,7 @@
17 17
18 18 <script>
19 19 import { transOrgFunc } from '@/config/common.js';
  20 +import api from '@/api/index.js'
20 21
21 22 export default {
22 23 data() {
... ... @@ -33,16 +34,12 @@ export default {
33 34 this.loadData();
34 35 },
35 36 methods: {
36   - loadData() {
37   - uni.$u.http
38   - .get('/yt/organization/me/list')
39   - .then(res => {
40   - if (res) {
41   - const list = transOrgFunc(res);
42   - this.tree = list;
43   - }
44   - })
45   - .catch(e => {});
  37 + async loadData() {
  38 + const res = await api.homeApi.getMeOrgListApi()
  39 + if (res) {
  40 + const list = transOrgFunc(res);
  41 + this.tree = list;
  42 + }
46 43 },
47 44 confirm(val) {
48 45 this.id = val[0].id;
... ...
  1 +<template>
  2 + <view>
  3 + <u-modal :showConfirmButton="false" :show="show" :title="title">
  4 + <view v-if="!bindPhone" class="login-phone">
  5 + <view class="form-row">
  6 + <u--input shape="circle" class="input" prefixIcon="account-fill" type="text" placeholder="请输入登录账号"
  7 + v-model="bindAccountForm.appUserKey"></u--input>
  8 + </view>
  9 + <view class="form-row item-bind">
  10 + <u--input class="input" shape="circle" prefixIcon="lock-fill" suffixIconStyle="color: #909399"
  11 + type="password" placeholder="请输入登录密码" v-model="bindAccountForm.appUserSecret"></u--input>
  12 + </view>
  13 + <view class="u-flex item-phone">
  14 + <view class="bind-phone-text" @click="openBindPhone">手机绑定</view>
  15 + </view>
  16 + </view>
  17 + <view v-else class="login-phone">
  18 + <view class="form-row">
  19 + <u--input shape="circle" class="input" type="text" v-model="bindPhoneForm.appUserKey"
  20 + placeholder="请输入手机号码" placeholder-style="font-weight:normal;color:#bbbbbb;"></u--input>
  21 + </view>
  22 + <view class="form-row row-top">
  23 + <u--input shape="circle" class="input" type="text" v-model="bindPhoneForm.appUserSecret"
  24 + placeholder="请输入验证码" placeholder-style="font-weight:normal;color:#bbbbbb;"></u--input>
  25 + <view style="color: #377dff" class="verify-code" :class="{ forhidden: readonly }"
  26 + @click="getVerifyCode">
  27 + {{ codeText }}
  28 + </view>
  29 + </view>
  30 + <view class="u-flex item-phone">
  31 + <view class="bind-phone-text" @click="openBindAccount">账号绑定</view>
  32 + </view>
  33 + </view>
  34 + <view class="bottom-content">
  35 + <view class="u-flex content">
  36 + <view class="cancel">
  37 + <u-button @click="$emit('cancelAccountModal')" type="info" shape="circle" text="取消"></u-button>
  38 + </view>
  39 + <view class="confrim">
  40 + <u-button @click="handleConfirm" type="primary" shape="circle" text="确认"></u-button>
  41 + </view>
  42 + </view>
  43 + </view>
  44 + </u-modal>
  45 + </view>
  46 +</template>
  47 +
  48 +<script>
  49 + var clear;
  50 + import fTabbar from '@/components/module/f-tabbar/f-tabbar';
  51 + import {
  52 + loginPasswordReg,
  53 + useShowToast
  54 + } from '@/plugins/utils.js'
  55 + import api from '@/api/index.js'
  56 +
  57 + export default {
  58 + components: {
  59 + fTabbar,
  60 + },
  61 + props: {
  62 + show: {
  63 + type: Boolean,
  64 + default: false
  65 + }
  66 + },
  67 + data() {
  68 + return {
  69 + readonly: false,
  70 + codeText: '获取验证码',
  71 + bindPhone: false,
  72 + title: '绑定账号',
  73 + bindAccountForm: {
  74 + appUserKey: '',
  75 + appUserSecret: ''
  76 + },
  77 + bindPhoneForm: {
  78 + appUserKey: '',
  79 + appUserSecret: ''
  80 + },
  81 + };
  82 + },
  83 + onLoad(e) {
  84 + // 隐藏原生的tabbar
  85 + uni.hideTabBar();
  86 + this.getOpenId = getApp().globalData.openId;
  87 + },
  88 + methods: {
  89 + resetFunc() {
  90 + this.bindPhone = false;
  91 + for (let i in this.bindAccountForm) Reflect.set(this.bindAccountForm, i, "")
  92 + for (let i in this.bindPhoneForm) Reflect.set(this.bindPhoneForm, i, "")
  93 + },
  94 + async handleBindForm(loginMethod, bindAccountForm, toastText) {
  95 + const data = {
  96 + loginMethod,
  97 + ...bindAccountForm,
  98 + platformName: 'WECHAT',
  99 + thirdUserId: this.getOpenId
  100 + };
  101 + const res = await api.loginApi.postThirdLoginApi(data)
  102 + if (!res) return
  103 + // 储存登录信息
  104 + let tokenInfo = {
  105 + refreshToken: res.refreshToken,
  106 + isToken: res.token
  107 + };
  108 + let userInfo = {
  109 + ...tokenInfo,
  110 + token: true, //token用于判断是否登录
  111 + isThirdLogin: false,
  112 + isThirdLoginAndNoDind: true
  113 + };
  114 + if (userInfo.token) {
  115 + this.setUserInfo(userInfo);
  116 + }
  117 + uni.$u.toast(toastText);
  118 + this.saveUserInfo();
  119 + this.$emit('cancelAccountModal')
  120 + },
  121 + handleConfirm() {
  122 + //账号绑定
  123 + if (!this.bindPhone) {
  124 + const validateValue = Object.values(this.bindAccountForm)
  125 + if (!validateValue[0]) return uni.$u.toast("请输入登录账号~");
  126 + if (!validateValue[1]) return uni.$u.toast("请输入登录密码~");
  127 + if (!loginPasswordReg.test(validateValue[1])) return useShowModal(
  128 + "密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~", )
  129 + this.handleBindForm('ACCOUNT', this.bindAccountForm, '账号绑定成功~')
  130 + } else {
  131 + //手机绑定
  132 + const phoneRegular = /^1\d{10}$/;
  133 + const verifyCodeReg = /^\d{6}$/;
  134 + const validateValue = Object.values(this.bindPhoneForm)
  135 + if (!validateValue[0]) return uni.$u.toast("请输入手机号码~");
  136 + if (!validateValue[1]) return uni.$u.toast("请输入验证码~");
  137 + if (!phoneRegular.test(validateValue[0])) return uni.$u.toast("手机号格式不正确~");
  138 + if (!verifyCodeReg.test(validateValue[1])) return uni.$u.toast("验证码格式不正确~");
  139 + this.handleBindForm('PHONE', this.bindPhoneForm, '手机绑定成功~')
  140 + }
  141 + },
  142 + async saveUserInfo() {
  143 + const userInfoRes = await api.loginApi.setUserInfoApi()
  144 + const plateInfoRes = await api.loginApi.setPlateInfoApi()
  145 + Promise.all([userInfoRes, plateInfoRes]).then(res => {
  146 + this.setUserInfo(res[0])
  147 + this.setPlateInfo(res[1])
  148 + })
  149 + },
  150 + openBindPhone() {
  151 + this.bindPhone = true;
  152 + },
  153 + openBindAccount() {
  154 + this.bindPhone = false;
  155 + },
  156 + //验证码按钮文字状态
  157 + verifyCodeCountDown() {
  158 + const _this = this;
  159 + this.readonly = true;
  160 + this.codeText = '60S后重新获取';
  161 + var s = 60;
  162 + clear = setInterval(() => {
  163 + s--;
  164 + _this.codeText = s + 'S后重新获取';
  165 + if (s <= 0) {
  166 + clearInterval(clear);
  167 + _this.codeText = '获取验证码';
  168 + _this.readonly = false;
  169 + }
  170 + }, 1000);
  171 + },
  172 + //获取验证码
  173 + async getVerifyCode() {
  174 + const phoneRegular = /^1\d{10}$/;
  175 + if (this.readonly) useShowToast('验证码已发送~')
  176 + if (!this.bindPhoneForm.appUserKey) return useShowToast('请输入手机号~')
  177 + if (!phoneRegular.test(this.bindPhoneForm.appUserKey)) return useShowToast('手机号格式不正确~')
  178 + await api.loginApi.postCodeApi(this.bindPhoneForm.appUserKey)
  179 + this.verifyCodeCountDown(); //开始倒计时
  180 + },
  181 + }
  182 + };
  183 +</script>
  184 +
  185 +<style lang="scss" scoped>
  186 + @import '../static/personal.scss';
  187 +</style>
\ No newline at end of file
... ...
  1 +<template>
  2 + <view>
  3 + <u-popup bgColor="transparent" :overlay="true" :show="show" mode="bottom">
  4 + <view class="u-flex logout-main">
  5 + <view class="main"><text style="color: #999999">您确定要退出当前账号吗?</text></view>
  6 + <view @click="$emit('logoutBtn')" class="main"><text style="color: #f95e5a">退出登录</text></view>
  7 + <view class="cancel-text"><text @click="$emit('closeLogoutPopup')" style="color: #3478f7">取消</text>
  8 + </view>
  9 + </view>
  10 + </u-popup>
  11 + </view>
  12 +</template>
  13 +
  14 +<script>
  15 + export default {
  16 + props: {
  17 + show: {
  18 + type: Boolean,
  19 + default: false
  20 + }
  21 + },
  22 + }
  23 +</script>
  24 +
  25 +<style lang="scss" scoped>
  26 + @import '../static/personal.scss';
  27 +</style>
\ No newline at end of file
... ...
  1 +const systemList = [{
  2 + url: '/sysnotify-subpackage/sys-notify/system-notify',
  3 + text: '系统通知',
  4 + leftIcon: '/static/sys-not.png',
  5 + rightIcon: '/static/arrow-right.png'
  6 + },
  7 + {
  8 + url: '/feedback-subpackage/feedback/feedback',
  9 + text: '意见反馈',
  10 + leftIcon: '/static/find-sugg.png',
  11 + rightIcon: '/static/arrow-right.png'
  12 + }
  13 +]
  14 +export {
  15 + systemList
  16 +}
\ No newline at end of file
... ...
1 1 <template>
2   - <view class="personal">
  2 + <view class="personal-page">
3 3 <!-- 公共组件-每个页面必须引入 -->
4 4 <public-module></public-module>
5   - <view class="headBox">
6   - <!-- #ifdef MP || APP-PLUS -->
7   - <!-- 登录 -->
  5 + <view class="header-box">
8 6 <view class="u-flex u-p-l-30 u-p-r-20 u-p-t-75 u-p-b-30">
9 7 <block v-if="userInfo.isToken || userInfo.isThirdLogin">
10   - <view @click.top="openPersonalInfo" class="u-m-r-20">
11   - <image class="avatar" mode="aspectFill"
12   - :src="userInfo.avatar || thirdObj.avatarUrl || '../../static/logo.png'"></image>
  8 + <view @click.top="navigatorPersonal" class="u-m-r-20">
  9 + <image class="avatar" mode="aspectFill" :src="setHeadImage"></image>
13 10 </view>
14   - <view class="u-flex-1" @click.top="openPersonalInfo">
  11 + <view class="u-flex-1" @click.top="navigatorPersonal">
15 12 <view class="nickName u-flex">
16 13 <view class="name u-m-r-10" v-if="userInfo.realName || userInfo.nickName">
17   - <text
18   - style="#FFFFFF;font-size: 18px;">{{ userInfo.realName || userInfo.nickName }}</text>
  14 + <text class="nick-name">{{ userInfo.realName || userInfo.nickName }}</text>
19 15 </view>
20   - <view v-if="userInfo.isThirdLogin" @click.stop="clickAccountFunc" class="detail"><text
  16 + <view v-if="userInfo.isThirdLogin" @click.stop="openBindAccountModal" class="detail"><text
21 17 class="text">绑定账号</text></view>
22 18 </view>
23   - <view style="color: #ffffff; font-size: 14px" v-if="userInfo.phoneNumber">
24   - {{ handlePhoneFunc(userInfo.phoneNumber || '') }}</view>
  19 + <view class="phone-number" v-if="userInfo.phoneNumber">
  20 + {{ handlePhoneFunc(userInfo.phoneNumber || '') }}
  21 + </view>
25 22 </view>
26 23 </block>
27 24 <block v-else>
28   - <view class="u-m-r-20" @click="openLoginFunc">
  25 + <view class="u-m-r-20" @click="navigatorLogin">
29 26 <view class="avatar u-flex">
30   - <image class="avatar" mode="aspectFill" src="../../static/logo.png"></image>
  27 + <image class="avatar" mode="aspectFill" src="/static/logo.png"></image>
31 28 </view>
32 29 </view>
33 30 <view class="u-flex-1">
34   - <view @click="openLoginFunc" class="u-font-lg click-login login-btn">请点击登录</view>
  31 + <view @click="navigatorLogin" class="u-font-lg click-login login-btn">请点击登录</view>
35 32 </view>
36 33 </block>
37   - <view v-if="userInfo.isToken" @click="openPersonalInfo">
  34 + <view v-if="userInfo.isToken" @click="navigatorPersonal">
38 35 <u-icon name="arrow-right" color="white" size="13"></u-icon>
39 36 </view>
40 37 </view>
41   - <!-- 登录 -->
42   - <!-- #endif -->
43 38 </view>
44 39 <view class="u-flex my-nav">
45 40 <view class="nav-main">
46   - <view @click="onTokenJump('/sysNotifySubPage/sysNotifyPage/systemNotify')" class="u-flex nav-link">
  41 + <view v-for="(item,index) in systemList" :key="index" @click="onTokenJump(item.url)" class="u-flex nav-link">
47 42 <view class="nav-image">
48   - <image class="image" src="../../static/sys-not.png"></image>
  43 + <image class="image" :src="item.leftIcon"></image>
49 44 </view>
50   - <view class="nav-center"><text class="text">系统通知</text></view>
  45 + <view class="nav-center"><text class="text">{{item.text}}</text></view>
51 46 <view class="nav-right">
52   - <image class="image" src="../../static/arrow-right.png"></image>
53   - </view>
54   - </view>
55   - <view @click="onTokenJump('/feedBackSubPage/feedback/feedback')" class="u-flex nav-link">
56   - <view class="nav-image">
57   - <image class="image" src="../../static/find-sugg.png"></image>
58   - </view>
59   - <view class="nav-center"><text class="text">意见反馈</text></view>
60   - <view class="nav-right">
61   - <image class="image" src="../../static/arrow-right.png"></image>
  47 + <image class="image" :src="item.rightIcon"></image>
62 48 </view>
63 49 </view>
64 50 </view>
65   - <view @click="onLoginoutFunc" class="u-flex" style="justify-content: center; width: 600rpx">
66   - <button class="submit" size="default" @click="onLoginoutFunc"><text class="text">退出账号</text></button>
  51 + <view @click="openLogoutPopup" class="u-flex logout-text">
  52 + <button class="submit" size="default" @click="openLogoutPopup"><text class="text">退出账号</text></button>
67 53 </view>
68 54 </view>
69 55 <!-- 绑定账号 -->
70   - <view>
71   - <u-modal :showConfirmButton="false" :show="show" :title="title">
72   - <view v-if="!bindPhone" class="loginPhone">
73   - <view class="form-row">
74   - <u--input shape="circle" class="input" prefixIcon="account-fill" type="text" placeholder="登录账号"
75   - v-model="bindAccountObj.appUserKey"></u--input>
76   - </view>
77   - <view class="form-row item-bind">
78   - <u--input class="input" shape="circle" prefixIcon="lock-fill" suffixIconStyle="color: #909399"
79   - type="password" placeholder="登录密码" v-model="bindAccountObj.appUserSecret"></u--input>
80   - </view>
81   - <view class="u-flex item-phone">
82   - <view class="phone-hide">手机验证码登录</view>
83   - <view class="bind-phone-text" @click="bindPhoneFunc">手机绑定</view>
84   - </view>
85   - </view>
86   - <view v-else class="loginPhone">
87   - <view class="form-row">
88   - <u--input shape="circle" class="input" type="text" v-model="bindPhoneObj.appUserKey"
89   - placeholder="请输入手机号码" placeholder-style="font-weight:normal;color:#bbbbbb;"></u--input>
90   - </view>
91   - <view class="form-row row-top">
92   - <u--input shape="circle" class="input" type="text" v-model="bindPhoneObj.appUserSecret"
93   - placeholder="请输入验证码" placeholder-style="font-weight:normal;color:#bbbbbb;"></u--input>
94   - <view style="color: #377dff" class="getvcode" :class="{ forhidden: readonly }"
95   - @click="getVcode">{{ codeText }}</view>
96   - </view>
97   - <view class="u-flex item-phone">
98   - <view class="phone-hide">手机验证码登录</view>
99   - <view class="bind-phone-text" @click="bindAccountFunc">账号绑定</view>
100   - </view>
101   - </view>
102   - <view class="bottom-content">
103   - <view class="u-flex content">
104   - <view class="cancel">
105   - <u-button @click="show = false" type="info" shape="circle" text="取消"></u-button>
106   - </view>
107   - <view class="confrim">
108   - <u-button @click="bindConfirm" type="primary" shape="circle" text="确认"></u-button>
109   - </view>
110   - </view>
111   - </view>
112   - </u-modal>
113   - </view>
114   - <!-- 退出登录 -->
115   - <view>
116   - <u-popup bgColor="transparent" :overlay="true" :show="showLogout" mode="bottom">
117   - <view class="u-flex logout-main">
118   - <view class="main"><text style="color: #999999">确定要退出当前账号?</text></view>
119   - <view @click.top="logoutBtn" class="main"><text style="color: #f95e5a">退出登录</text></view>
120   - <view class="main1"><text @click.top="closeLogout" style="color: #3478f7">取消</text></view>
121   - </view>
122   - </u-popup>
123   - </view>
  56 + <bind-account-modal ref="bindAccountRef" :show="showBindAccount" @cancelAccountModal="handleCancelAccountModal" />
124 57 <!-- 退出登录 -->
  58 + <logout-account-modal ref="logoutAccountRef" :show="showLogout" @logoutBtn="logoutBtn" @closeLogoutPopup="closeLogoutPopup"/>
125 59 <f-tabbar></f-tabbar>
126 60 </view>
127 61 </template>
128 62
129 63 <script>
130   - var clear;
131   - import base from '@/config/baseUrl';
132 64 import fTabbar from '@/components/module/f-tabbar/f-tabbar';
133   - import fNavbar from '@/components/module/f-navbar/f-navbar';
134   - import {
135   - mapState,
136   - mapMutations
137   - } from 'vuex';
138   - import { loginPasswordReg } from '@/plugins/utils.js'
  65 + import bindAccountModal from './components/bind-account-modal.vue'
  66 + import logoutAccountModal from './components/logout-account-modal.vue'
  67 + import { mapState,mapMutations } from 'vuex';
  68 + import { useNavigateTo,useReLaunch,useShowModal } from '@/plugins/utils.js'
  69 + import { systemList } from './config/data.js'
139 70
140 71 export default {
141 72 components: {
142 73 fTabbar,
143   - fNavbar
  74 + bindAccountModal,
  75 + logoutAccountModal,
144 76 },
145 77 data() {
146 78 return {
147   - PrimaryColor: '#0079fe', //主题色
  79 + showBindAccount: false,
148 80 showLogout: false,
149   - readonly: false,
150   - codeText: '获取验证码',
151   - tips: '验证码',
152   - bindPhone: false,
153   - show: false,
154   - title: '绑定账号',
155   - systemInfo: base.systemInfo,
156   - PrimaryButtonColor: '#0079fe', //主题色
157   - bindAccountObj: {
158   - appUserKey: '',
159   - appUserSecret: ''
160   - },
161   - bindPhoneObj: {
162   - appUserKey: '',
163   - appUserSecret: ''
164   - },
165 81 thirdObj: {},
166   - getOpenId: '',
167   - mpOwnConfig: {}
  82 + systemList,
168 83 };
169 84 },
170   - mounted() {
171   - this.getPlateForm();
172   - },
173 85 onLoad(e) {
174 86 // 隐藏原生的tabbar
175 87 uni.hideTabBar();
176   - // if (e.obj != null) {
177   - // const params = JSON.parse(decodeURIComponent(e.obj));
178   - // // uni.$u.toast('eee', params.avatarUrl);
179   - // this.thirdObj = params;
180   - // }
181   - this.getOpenId = getApp().globalData.openId;
182 88 },
183 89 computed: {
184   - ...mapState(['userInfo', 'plateInfo'])
  90 + ...mapState(['userInfo', 'plateInfo']),
  91 + setHeadImage() {
  92 + return this.userInfo.avatar || this.thirdObj.avatarUrl || '/static/logo.png'
  93 + }
185 94 },
186 95 methods: {
187   - //获取平台定制信息
188   - getPlateForm() {
189   - uni.$u.http.get('/yt/app_design/get').then(res => {
190   - if (res) {
191   - this.mpOwnConfig = {
192   - bg: res.background,
193   - logo: res.logo,
194   - name: res.name
195   - };
196   - }
197   - });
198   - },
  96 + ...mapMutations(['emptyUserInfo', 'setUserInfo', 'setPlateInfo']),
199 97 handlePhoneFunc(e) {
200   - //前三后四位显示
  98 + //手机号前三后四位显示
201 99 const result = /^(\d{3})\d{4}(\d{4})$/;
202 100 const y = e.toString().replace(result, '$1****$2');
203 101 return y;
204 102 },
205   - ...mapMutations(['emptyUserInfo', 'setUserInfo', 'setPlateInfo']),
206 103 // 跳转前判断登录
207 104 onTokenJump(url) {
  105 + if(!url) return
208 106 this.judgeLogin(() => {
209   - uni.navigateTo({
210   - url: url
211   - });
  107 + useNavigateTo(url)
212 108 });
213 109 },
214 110 onJump(url) {
215   - uni.navigateTo({
216   - url: url
217   - });
  111 + if(!url) return
  112 + useNavigateTo(url)
218 113 },
219   - openLoginFunc() {
220   - uni.navigateTo({
221   - url: '/publicLoginSubPage/public/login'
222   - });
  114 + navigatorLogin() {
  115 + useNavigateTo('/login-subpackage/public/login')
223 116 },
224   - openPersonalInfo() {
225   - let obj = {
  117 + navigatorPersonal() {
  118 + let data = {
226 119 data: this.userInfo,
227 120 third: this.thirdObj
228 121 };
229   - uni.navigateTo({
230   - url: '/publicLoginSubPage/other/set?data=' + JSON.stringify(obj)
231   - });
232   - },
233   - clickAccountFunc() {
234   - this.show = true;
235   - this.resetFunc();
236   - },
237   - resetFunc() {
238   - this.bindPhone = false;
239   - this.bindAccountObj = {};
240   - this.bindPhoneObj = {};
241   - },
242   - bindConfirm() {
243   - //需要绑定
244   - if (!this.bindPhone) {
245   - if (this.bindAccountObj.appUserKey == '' || this.bindAccountObj.appUserKey == undefined) {
246   - return uni.$u.toast('请输入登录账号~');
247   - }
248   - if (this.bindAccountObj.appUserSecret == '') {
249   - uni.showToast({
250   - title: '请输入登录密码~',
251   - icon: 'none'
252   - });
253   - return;
254   - } else if (!loginPasswordReg.test(this.bindAccountObj.appUserSecret)) {
255   - uni.showModal({
256   - title: '提示',
257   - content: '密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~',
258   - showCancel: false
259   - });
260   - return;
261   - }
262   - const postData = {
263   - loginMethod: 'ACCOUNT',
264   - ...this.bindAccountObj,
265   - platformName: 'WECHAT',
266   - thirdUserId: this.getOpenId
267   - };
268   - uni.$u.http
269   - .post('/yt/third/bind', postData)
270   - .then(res => {
271   - if (res) {
272   - this.show = false;
273   - // 储存登录信息
274   - let resObj = {
275   - refreshToken: res.refreshToken,
276   - isToken: res.token
277   - };
278   - let userInfo = {
279   - ...resObj,
280   - token: true, //token用于判断是否登录
281   - isThirdLogin: false,
282   - isThirdLoginAndNoDind: true
283   - };
284   - if (userInfo.token) {
285   - this.setUserInfo(userInfo);
286   - }
287   - uni.showToast({
288   - title: '账号绑定成功~',
289   - icon: 'none'
290   - });
291   - this.saveUserInfo();
292   - }
293   - })
294   - .catch(e => {
295   - let msg = e.message || e.data?.message;
296   - // if (msg == undefined) {
297   - // msg = '';
298   - // }
299   - if (msg) uni.$u.toast(msg);
300   - this.show = true;
301   - });
302   - } else {
303   - const phoneRegular = /^1\d{10}$/;
304   - if (this.bindPhoneObj.appUserKey == '') {
305   - uni.showToast({
306   - title: '请输入手机号码~',
307   - icon: 'none'
308   - });
309   - return;
310   - } else if (!phoneRegular.test(this.bindPhoneObj.appUserKey)) {
311   - uni.showToast({
312   - title: '手机号格式不正确~',
313   - icon: 'none'
314   - });
315   - return;
316   - }
317   - if (this.bindPhoneObj.appUserSecret == '') {
318   - uni.showToast({
319   - title: '请输入验证码~',
320   - icon: 'none'
321   - });
322   - return;
323   - } else if (!/^\d{6}$/.test(this.bindPhoneObj.appUserSecret)) {
324   - uni.showToast({
325   - title: '验证码格式不正确~',
326   - icon: 'none'
327   - });
328   - return;
329   - }
330   - const postData = {
331   - loginMethod: 'PHONE',
332   - ...this.bindPhoneObj,
333   - platformName: 'WECHAT',
334   - thirdUserId: this.getOpenId
335   - };
336   - uni.$u.http
337   - .post('/yt/third/bind', postData)
338   - .then(res => {
339   - this.show = false;
340   - // 储存登录信息
341   - let resObj = {
342   - refreshToken: res.refreshToken,
343   - isToken: res.token
344   - };
345   - let userInfo = {
346   - ...resObj,
347   - token: true, //token用于判断是否登录
348   - isThirdLogin: false,
349   - isThirdLoginAndNoDind: true
350   - };
351   - if (userInfo.token) {
352   - this.setUserInfo(userInfo);
353   - }
354   - uni.showToast({
355   - title: '手机绑定成功~',
356   - icon: 'none'
357   - });
358   - this.saveUserInfo();
359   - })
360   - .catch(e => {
361   - let msg = e.message || e.data?.message;
362   - // msg = '';
363   - // }
364   - if (msg) uni.$u.toast(msg);
365   - this.show = true;
366   - });
367   - }
  122 + useNavigateTo('/login-subpackage/other/set?data=', data)
368 123 },
369   - saveUserInfo() {
370   - //储存个人信息
371   - uni.$u.http.get('/yt/user/me/info').then(res => {
372   - if (res) {
373   - this.setUserInfo(res);
374   - }
375   - });
376   - //储存平台信息
377   - uni.$u.http.get('/yt/platform/get').then(res => {
378   - if (res) {
379   - this.setPlateInfo(res);
380   - }
381   - });
  124 + openBindAccountModal() {
  125 + this.showBindAccount = true;
  126 + this.$refs.bindAccountRef.resetFunc()
382 127 },
383   - bindPhoneFunc() {
384   - this.bindPhone = true;
385   - },
386   - bindAccountFunc() {
387   - this.bindPhone = false;
388   - },
389   - //验证码按钮文字状态
390   - getCodeState() {
391   - const _this = this;
392   - this.readonly = true;
393   - this.codeText = '60S后重新获取';
394   - var s = 60;
395   - clear = setInterval(() => {
396   - s--;
397   - _this.codeText = s + 'S后重新获取';
398   - if (s <= 0) {
399   - clearInterval(clear);
400   - _this.codeText = '获取验证码';
401   - _this.readonly = false;
402   - }
403   - }, 1000);
404   - },
405   - //获取验证码
406   - getVcode() {
407   - if (this.readonly) {
408   - uni.showToast({
409   - title: '验证码已发送~',
410   - icon: 'none'
411   - });
412   - return;
413   - }
414   - if (this.bindPhoneObj.appUserKey == '') {
415   - uni.showToast({
416   - title: '请输入手机号~',
417   - icon: 'none'
418   - });
419   - return;
420   - }
421   - const phoneRegular = /^1\d{10}$/;
422   - if (!phoneRegular.test(this.bindPhoneObj.appUserKey)) {
423   - uni.showToast({
424   - title: '手机号格式不正确~',
425   - icon: 'none'
426   - });
427   - return;
428   - }
429   - let httpData = {};
430   - // 获取验证码接口
431   - uni.$u.http.post(`/yt/noauth/send_login_code/${this.bindPhoneObj.appUserKey}`).then(res => {
432   - if (res) {
433   - this.getCodeState(); //开始倒计时
434   - }
435   - });
436   - },
437   - onLoginoutFunc() {
  128 + openLogoutPopup() {
438 129 this.showLogout = true;
439 130 },
440   - closeLogout() {
  131 + closeLogoutPopup() {
441 132 this.showLogout = false;
442 133 },
443 134 logoutBtn() {
444   - let that = this;
445   - uni.showModal({
446   - title: '退出登录',
447   - content: '你确定退出登录吗?',
448   - success(res) {
449   - if (res.confirm) {
450   - that.emptyUserInfo();
451   - that.showLogout = false;
452   - setTimeout(() => {
453   - uni.navigateTo({
454   - url: '/publicLoginSubPage/public/login'
455   - });
456   - }, 500);
457   - } else if (res.cancel) {}
  135 + const that = this
  136 + useShowModal('您确定要退出登录吗?', '退出登录', '确定').then(res => {
  137 + if (res.confirm) {
  138 + that.emptyUserInfo();
  139 + that.showLogout = false;
  140 + //不能使用navigatorTo,会显示返回按钮
  141 + useReLaunch('/login-subpackage/public/login')
458 142 }
459   - });
  143 + })
  144 + },
  145 + handleCancelAccountModal() {
  146 + this.showBindAccount = false;
460 147 }
461 148 }
462 149 };
... ... @@ -464,4 +151,4 @@
464 151
465 152 <style lang="scss" scoped>
466 153 @import './static/personal.scss';
467   -</style>
  154 +</style>
\ No newline at end of file
... ...
1   -.personal {
  1 +.personal-page {
2 2 background-color: #ffffff;
3 3 height: 100vh;
4 4 }
5   -.headBox {
  5 +.header-box {
6 6 background: linear-gradient(90deg, #8c9ef2 0%, #5f6ee6 100%);
7 7 border-top: 0.01px solid #f5f5f5;
8 8 height: 250rpx;
... ... @@ -51,6 +51,10 @@
51 51 color: #fff;
52 52 font-weight: bold;
53 53 font-size: 32rpx;
  54 + .nick-name{
  55 + color:#FFFFFF;
  56 + font-size: 18px;
  57 + }
54 58 }
55 59
56 60 .placardVip {
... ... @@ -62,7 +66,10 @@
62 66 border-radius: 4rpx;
63 67 }
64 68 }
65   -
  69 + .phone-number{
  70 + color: #ffffff;
  71 + font-size: 14px;
  72 + }
66 73 .detail {
67 74 color: #fff;
68 75 font-size: 24rpx;
... ... @@ -97,6 +104,10 @@
97 104 justify-content: space-between;
98 105 height: 147rpx;
99 106 }
  107 + .logout-text{
  108 + justify-content: center;
  109 + width: 600rpx
  110 + }
100 111 .submit {
101 112 margin-top: 60rpx;
102 113 background: linear-gradient(90deg, #5dc2fc 0%, #377dff 100%);
... ... @@ -169,7 +180,7 @@
169 180 border: none;
170 181 }
171 182
172   -.loginPhone {
  183 +.login-phone {
173 184 .form-row {
174 185 position: relative;
175 186 // background: #f7f9ff;
... ... @@ -186,7 +197,7 @@
186 197 font-weight: bold;
187 198 }
188 199
189   - .getvcode {
  200 + .verify-code {
190 201 font-size: 26rpx;
191 202 height: 50rpx;
192 203 color: #333;
... ... @@ -218,11 +229,9 @@
218 229 height: 80rpx;
219 230 }
220 231 .item-phone {
221   - flex-direction: row;
  232 + display: flex;
  233 + justify-content: center;
222 234 margin-top: 20rpx;
223   - .phone-hide {
224   - visibility: hidden;
225   - }
226 235 .bind-phone-text {
227 236 color: #0079fe;
228 237 font-size: 14px;
... ... @@ -256,7 +265,7 @@
256 265 text-align: center;
257 266 line-height: 86rpx;
258 267 }
259   - .main1 {
  268 + .cancel-text {
260 269 width: 669rpx;
261 270 height: 100rpx;
262 271 text-align: center;
... ...
... ... @@ -4,156 +4,281 @@ const DATE_TIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
4 4
5 5 /**
6 6 登录密码正则验证
7   - 最短8位,最长16位
8   - 必须包含1个数字
9   - 必须包含1个小写字母
10   - 必须包含1个大写字母
  7 + 最短8位,最长16位
  8 + 必须包含1个数字
  9 + 必须包含1个小写字母
  10 + 必须包含1个大写字母
11 11 必须包含1个特殊字符
12 12 */
13   -export const loginPasswordReg =/^.*(?=.{6,16})(?=.*\d)(?=.*[A-Z]{1,})(?=.*[a-z]{1,})(?=.*[_!@#$%^&*?\(\)]).*$/;
  13 +export const loginPasswordReg =
  14 + /^.*(?=.{6,16})(?=.*\d)(?=.*[A-Z]{1,})(?=.*[a-z]{1,})(?=.*[_!@#$%^&*?\(\)]).*$/;
14 15
15 16 //手机号中间4位为*
16   -Vue.filter("phone", function (val) {
17   - var tel = val;
18   - tel = "" + tel;
19   - var telShort = tel.replace(tel.substring(3, 7), "****");
20   - return telShort;
  17 +Vue.filter("phone", function(val) {
  18 + var tel = val;
  19 + tel = "" + tel;
  20 + var telShort = tel.replace(tel.substring(3, 7), "****");
  21 + return telShort;
21 22 });
22 23 //获取系统信息、判断ipX安全距离
23   -export const getTabbarHeight = function () {
24   - var systemInfo = uni.getSystemInfoSync();
25   - var data = {
26   - ...systemInfo,
27   - tabbarH: 50, //tabbar高度--单位px
28   - tabbarPaddingB: 0, //tabbar底部安全距离高度--单位px
29   - device: systemInfo.system.indexOf("iOS") != -1 ? "iOS" : "Android", //苹果或者安卓设备
30   - };
31   - let modelArr = [
32   - "10,3",
33   - "10,6",
34   - "X",
35   - "XR",
36   - "XS",
37   - "11",
38   - "12",
39   - "13",
40   - "14",
41   - "15",
42   - "16",
43   - ];
44   - let model = systemInfo.model;
45   - model &&
46   - modelArr.forEach((item) => {
47   - //适配iphoneX以上的底部,给tabbar一定高度的padding-bottom
48   - if (
49   - model.indexOf(item) != -1 &&
50   - (model.indexOf("iPhone") != -1 || model.indexOf("iphone") != -1)
51   - ) {
52   - data.tabbarH = 70;
53   - data.tabbarPaddingB = 20;
54   - }
55   - });
56   - return data;
  24 +export const getTabbarHeight = function() {
  25 + var systemInfo = uni.getSystemInfoSync();
  26 + var data = {
  27 + ...systemInfo,
  28 + tabbarH: 50, //tabbar高度--单位px
  29 + tabbarPaddingB: 0, //tabbar底部安全距离高度--单位px
  30 + device: systemInfo.system.indexOf("iOS") != -1 ? "iOS" : "Android", //苹果或者安卓设备
  31 + };
  32 + let modelArr = [
  33 + "10,3",
  34 + "10,6",
  35 + "X",
  36 + "XR",
  37 + "XS",
  38 + "11",
  39 + "12",
  40 + "13",
  41 + "14",
  42 + "15",
  43 + "16",
  44 + ];
  45 + let model = systemInfo.model;
  46 + model &&
  47 + modelArr.forEach((item) => {
  48 + //适配iphoneX以上的底部,给tabbar一定高度的padding-bottom
  49 + if (
  50 + model.indexOf(item) != -1 &&
  51 + (model.indexOf("iPhone") != -1 || model.indexOf("iphone") != -1)
  52 + ) {
  53 + data.tabbarH = 70;
  54 + data.tabbarPaddingB = 20;
  55 + }
  56 + });
  57 + return data;
57 58 };
58 59
59 60 // px转upx
60   -export const px2upx = function (n) {
61   - return n / (uni.upx2px(n) / n);
  61 +export const px2upx = function(n) {
  62 + return n / (uni.upx2px(n) / n);
62 63 };
63 64
64 65 // 小程序获取定位权限判断
65 66 // isOpenSetting 默认false:不检验授权,true:检验授权后获取地址
66 67 function getMpLocation(successCallback, errCallback, isOpenSetting) {
67   - uni.getSetting({
68   - success: (res) => {
69   - if (res.authSetting["scope.userLocation"] || !isOpenSetting) {
70   - uni.getLocation({
71   - // #ifndef MP-ALIPAY
72   - type: "gcj02",
73   - // #endif
74   - success(res) {
75   - console.log("successCallback");
76   - successCallback(res);
77   - },
78   - fail(err) {
79   - console.log("位置信息错误", err);
80   - errCallback("位置信息获取失败");
81   - },
82   - });
83   - } else {
84   - errCallback("“位置信息”未授权");
85   - isOpenSetting &&
86   - uni.showModal({
87   - title: "提示",
88   - content: "请先在设置页面打开“位置信息”使用权限",
89   - confirmText: "去设置",
90   - cancelText: "再逛会",
91   - success: (res) => {
92   - if (res.confirm) {
93   - uni.openSetting();
94   - }
95   - },
96   - });
97   - }
98   - },
99   - });
  68 + uni.getSetting({
  69 + success: (res) => {
  70 + if (res.authSetting["scope.userLocation"] || !isOpenSetting) {
  71 + uni.getLocation({
  72 + // #ifndef MP-ALIPAY
  73 + type: "gcj02",
  74 + // #endif
  75 + success(res) {
  76 + console.log("successCallback");
  77 + successCallback(res);
  78 + },
  79 + fail(err) {
  80 + console.log("位置信息错误", err);
  81 + errCallback("位置信息获取失败");
  82 + },
  83 + });
  84 + } else {
  85 + errCallback("“位置信息”未授权");
  86 + isOpenSetting &&
  87 + uni.showModal({
  88 + title: "提示",
  89 + content: "请先在设置页面打开“位置信息”使用权限",
  90 + confirmText: "去设置",
  91 + cancelText: "再逛会",
  92 + success: (res) => {
  93 + if (res.confirm) {
  94 + uni.openSetting();
  95 + }
  96 + },
  97 + });
  98 + }
  99 + },
  100 + });
100 101 }
101 102 // 获取地址信息
102 103 let locationAuthorize = true;
103   -export const getAppLatLon = function (
104   - successCallback,
105   - errCallback,
106   - isOpenSetting
  104 +export const getAppLatLon = function(
  105 + successCallback,
  106 + errCallback,
  107 + isOpenSetting
107 108 ) {
108   - const _this = this;
109   - // #ifdef MP-WEIXIN
110   - if (locationAuthorize && isOpenSetting) {
111   - uni.authorize({
112   - scope: "scope.userLocation",
113   - success: () => {
114   - getMpLocation(successCallback, errCallback, isOpenSetting);
115   - locationAuthorize = false;
116   - },
117   - fail: () => {
118   - locationAuthorize = false;
119   - },
120   - });
121   - } else {
122   - getMpLocation(successCallback, errCallback, isOpenSetting);
123   - }
124   - // #endif
125   - // #ifdef MP-ALIPAY
126   - getMpLocation(successCallback, errCallback, false);
127   - // #endif
128   - // #ifdef H5
129   - uni.getLocation({
130   - type: "gcj02",
131   - success(res) {
132   - console.log("successCallback");
133   - successCallback(res);
134   - },
135   - fail(err) {
136   - console.log("位置信息错误", err);
137   - errCallback("位置信息获取失败");
138   - },
139   - });
140   - // #endif
  109 + const _this = this;
  110 + // #ifdef MP-WEIXIN
  111 + if (locationAuthorize && isOpenSetting) {
  112 + uni.authorize({
  113 + scope: "scope.userLocation",
  114 + success: () => {
  115 + getMpLocation(successCallback, errCallback, isOpenSetting);
  116 + locationAuthorize = false;
  117 + },
  118 + fail: () => {
  119 + locationAuthorize = false;
  120 + },
  121 + });
  122 + } else {
  123 + getMpLocation(successCallback, errCallback, isOpenSetting);
  124 + }
  125 + // #endif
  126 + // #ifdef MP-ALIPAY
  127 + getMpLocation(successCallback, errCallback, false);
  128 + // #endif
  129 + // #ifdef H5
  130 + uni.getLocation({
  131 + type: "gcj02",
  132 + success(res) {
  133 + console.log("successCallback");
  134 + successCallback(res);
  135 + },
  136 + fail(err) {
  137 + console.log("位置信息错误", err);
  138 + errCallback("位置信息获取失败");
  139 + },
  140 + });
  141 + // #endif
141 142 };
142 143
143 144 export function formatToDate(date = undefined, format = DATE_TIME_FORMAT) {
144   - return moment(date).format(format);
  145 + return moment(date).format(format);
145 146 }
146 147
147 148 //封装uniapp跳转 navigateTo
148   -export const useNavigateTo=(path,param)=>{
149   - if (!path) return
150   - if(param){
  149 +export const useNavigateTo = (path, param) => {
  150 + if (!path) return;
  151 + if (param) {
151 152 uni.navigateTo({
152   - url: path + encodeURIComponent(JSON.stringify(param))
  153 + url: path + encodeURIComponent(JSON.stringify(param)),
153 154 });
154   - }else{
  155 + } else {
155 156 uni.navigateTo({
156   - url:path
  157 + url: path,
  158 + });
  159 + }
  160 +};
  161 +
  162 +//封装uniapp跳转 reLaunch
  163 +export const useReLaunch = (path, param) => {
  164 + if (!path) return;
  165 + if (param) {
  166 + uni.reLaunch({
  167 + url: path + encodeURIComponent(JSON.stringify(param)),
  168 + });
  169 + } else {
  170 + uni.reLaunch({
  171 + url: path,
  172 + });
  173 + }
  174 +};
  175 +
  176 +//封装uniapp showToast
  177 +export const useShowToast = (title, duration = 500, mask = false) => {
  178 + return new Promise((resolve, reject) => {
  179 + uni.showToast({
  180 + title: title,
  181 + icon: "none",
  182 + duration,
  183 + mask,
  184 + success: (res) => {
  185 + resolve(res);
  186 + },
  187 + fail: (err) => {
  188 + reject(err);
  189 + },
  190 + });
  191 + });
  192 +};
  193 +
  194 +//封装uniapp showModal
  195 +export const useShowModal = (content, title = "提示", confirmText = "确定") => {
  196 + return new Promise((resolve, reject) => {
  197 + uni.showModal({
  198 + title,
  199 + content: content,
  200 + confirmText,
  201 + success: (res) => {
  202 + resolve(res);
  203 + },
  204 + fail: (err) => {
  205 + reject(err);
  206 + },
  207 + });
  208 + });
  209 +};
  210 +
  211 +//封装uniapp uni.uploadFile
  212 +export const useUploadFile = (
  213 + url,
  214 + filePath,
  215 + name = "file",
  216 + formData,
  217 + header
  218 +) => {
  219 + return new Promise(function(resolve, reject) {
  220 + uni.uploadFile({
  221 + url, //请求接口地址
  222 + filePath, //文件地址
  223 + name,
  224 + formData,
  225 + header,
  226 + success(res) {
  227 + resolve(res);
  228 + },
  229 + fail(err) {
  230 + reject(res);
  231 + },
  232 + });
  233 + });
  234 +};
  235 +
  236 +//封装uniapp uniapp.chooseImage
  237 +export const useChooseImage = (data) => {
  238 + return new Promise((resolve, reject) => {
  239 + uni.chooseImage({
  240 + count: data.count || 1, //默认1
  241 + sizeType: data.sizeType || ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有
  242 + sourceType: data.sourceType || ["album", "camera"], //从相册选择
  243 + success: function(res) {
  244 + resolve(res.tempFiles);
  245 + },
  246 + fail: (err) => {
  247 + reject({
  248 + errMsg: err.errMsg,
  249 + errCode: err.errCode,
  250 + statusCode: 0,
  251 + });
  252 + },
157 253 });
  254 + });
  255 +};
  256 +
  257 +//文件上传校验
  258 +export const useFileValidate = (file, fileSize) => {
  259 + if (file.size > fileSize) {
  260 + useShowToast("上传的图片大小不能超过5M", 2000, true);
  261 + throw Error("上传的图片大小不能超过5M");
158 262 }
159   -}
\ No newline at end of file
  263 + const fileTxt = file.path.split(".").pop();
  264 + const fileTypeList = ["jpg", "jpeg", "png"];
  265 + if (!fileTypeList.includes(fileTxt)) {
  266 + useShowToast("请上传指定图片类型(jpg、jpeg、png)", 2000, true);
  267 + throw Error("请上传指定图片类型(jpg、jpeg、png)");
  268 + }
  269 +};
  270 +
  271 +//封装uniapp uni.pageScrollTo
  272 +export const usePageScrollTo = (scrollTop, duration) => {
  273 + uni.pageScrollTo({
  274 + scrollTop, // 滚动到页面的目标位置 这个是滚动到顶部, 0
  275 + duration, // 滚动动画的时长
  276 + });
  277 +};
  278 +
  279 +//封装uniapp跳转 navigateBack
  280 +export const useNavigateBack = (delta) => {
  281 + uni.navigateBack({
  282 + delta
  283 + });
  284 +};
\ No newline at end of file
... ...
1   -<template>
2   - <view class="code-page">
3   - <!-- 公共组件-每个页面必须引入 -->
4   - <public-module></public-module>
5   - <view class="f__login">
6   - <view class="loginPhone">
7   - <view class="phone-main" style="margin-top: 240rpx;">
8   - <text class="text">手机验证码登录</text>
9   - <view class="circleStyle"></view>
10   - </view>
11   - <view class="form-row"><u-input v-model="phone" type="number" placeholder="请输入手机号码" border="bottom"></u-input></view>
12   - <view class="form-row">
13   - <u-input type="number" v-model="vCode" placeholder="请输入验证码" border="bottom">
14   - <template slot="suffix">
15   - <view @click="getVcode" class="getvcode">{{ codeText }}</view>
16   - </template>
17   - </u-input>
18   - </view>
19   - <button class="submit" size="default" @click="onSubmit"><text class="text">登录</text></button>
20   - <view class="u-flex account-style"><view class="content" @click="openAccountFunc">账号密码登录</view></view>
21   - <view class="circleStyleBottom"></view>
22   - </view>
23   - </view>
24   - </view>
25   -</template>
26   -
27   -<script>
28   -var clear;
29   -import { mapState, mapMutations, mapActions } from 'vuex';
30   -export default {
31   - data() {
32   - return {
33   - readonly: false,
34   - codeText: '发送验证码',
35   - phone: '', //号码
36   - vCode: '' //验证码
37   - };
38   - },
39   - methods: {
40   - ...mapMutations(['setUserInfo']),
41   - ...mapActions(['updateBadgeTotal']),
42   - //验证码按钮文字状态
43   - getCodeState() {
44   - const _this = this;
45   - this.readonly = true;
46   - this.codeText = '60s后重新获取';
47   - var s = 60;
48   - clear = setInterval(() => {
49   - s--;
50   - _this.codeText = s + 's后重新获取';
51   - if (s <= 0) {
52   - clearInterval(clear);
53   - _this.codeText = '发送验证码';
54   - _this.readonly = false;
55   - }
56   - }, 1000);
57   - },
58   - //获取验证码
59   - getVcode() {
60   - if (this.readonly) {
61   - uni.showToast({
62   - title: '验证码已发送~',
63   - icon: 'none'
64   - });
65   - return;
66   - }
67   - if (this.phone == '') {
68   - uni.showToast({
69   - title: '请输入手机号~',
70   - icon: 'none'
71   - });
72   - return;
73   - }
74   - const phoneRegular = /^1\d{10}$/;
75   - if (!phoneRegular.test(this.phone)) {
76   - uni.showToast({
77   - title: '手机号格式不正确~',
78   - icon: 'none'
79   - });
80   - return;
81   - }
82   - // 获取验证码接口
83   - uni.$u.http
84   - .post(`/yt/noauth/send_login_code/${this.phone}`)
85   - .then(res => {
86   - if (res) {
87   - this.getCodeState(); //开始倒计时
88   - }
89   - })
90   - .catch(err => {
91   - uni.showToast({
92   - title: err.data.msg,
93   - icon: 'none'
94   - });
95   - });
96   - },
97   - onSubmit() {
98   - const phoneRegular = /^1\d{10}$/;
99   - if (this.phone == '') {
100   - uni.showToast({
101   - title: '请输入手机号码~',
102   - icon: 'none'
103   - });
104   - return;
105   - } else if (!phoneRegular.test(this.phone)) {
106   - uni.showToast({
107   - title: '手机号格式不正确~',
108   - icon: 'none'
109   - });
110   - return;
111   - }
112   - if (this.vCode == '') {
113   - uni.showToast({
114   - title: '请输入验证码~',
115   - icon: 'none'
116   - });
117   - return;
118   - } else if (!/^\d{6}$/.test(this.vCode)) {
119   - uni.showToast({
120   - title: '验证码格式不正确~',
121   - icon: 'none'
122   - });
123   - return;
124   - }
125   - let httpData = {
126   - code: this.vCode,
127   - phoneNumber: this.phone
128   - };
129   - uni.$u.http.post('/yt/auth/code/login', httpData).then(res => {
130   - if (res) {
131   - // 储存登录信息
132   - let resObj = {
133   - refreshToken: res.refreshToken,
134   - isToken: res.token
135   - };
136   - let userInfo = {
137   - ...resObj,
138   - token: true, //token用于判断是否登录
139   - isThirdLogin: false
140   - };
141   - if (userInfo.token) {
142   - this.setUserInfo(userInfo);
143   - }
144   - uni.showToast({
145   - title: '登录成功~',
146   - icon: 'none'
147   - }).then(async res => {
148   - this.saveUserInfo();
149   - await this.getAlarmTotalData();
150   - uni.reLaunch({
151   - url: '/pages/personal/personal'
152   - });
153   - });
154   - }
155   - });
156   - },
157   - async getAlarmTotalData() {
158   - const res = await uni.$u.http.get('/yt/homepage/app?login=true');
159   - if (res) {
160   - //异步实时更新告警徽标数
161   - this.updateBadgeTotal(res.totalAlarm?.activedAlarm);
162   - }
163   - },
164   - saveUserInfo() {
165   - //储存个人信息
166   - uni.$u.http.get('/yt/user/me/info').then(res => {
167   - if (res) {
168   - this.setUserInfo(res);
169   - }
170   - });
171   - },
172   - openAccountFunc() {
173   - uni.navigateTo({
174   - url: '../public/login'
175   - });
176   - }
177   - }
178   -};
179   -</script>
180   -
181   -<style lang="scss" scoped>
182   -@import './static/code.scss';
183   -</style>
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: !nextStatus ? '#0079fe' : '' }" class="item">1.验证手机号码</view>
6   - <view :style="{ color: !nextStatus ? '' : '#0079fe' }" class="item">2.设置新密码</view>
7   - </view>
8   - <view v-if="!nextStatus" style="margin-top: 40rpx;" class="f__login">
9   - <view class="loginPhone">
10   - <view class="form-row">
11   - <u-input v-model="phone" type="number" placeholder="请输入手机号码" border="bottom" />
12   - </view>
13   - <view class="form-row">
14   - <u-input type="number" v-model="vCode" placeholder="请输入验证码" border="bottom">
15   - <template slot="suffix">
16   - <view @click="getVcode" class="getvcode">{{ 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 style="margin-top: 40rpx;" class="f__login">
24   - <view class="loginPhone">
25   - <view class="form-row u-flex">
26   - <u-input v-model="password" :password="showPasswordF" placeholder="请设置6-20位新的登录密码" border="bottom">
27   - <template slot="suffix">
28   - <view @click="showPasswordMode" style="padding: 10rpx">
29   - <u-icon width="18" height="14" :name="
30   - showPasswordF ? '/static/eye-hide.png' : '/static/eye.png'
31   - "></u-icon>
32   - </view>
33   - </template>
34   - </u-input>
35   - </view>
36   - <view class="form-row u-flex">
37   - <u-input v-model="rePassword" :password="showPasswordS" placeholder="请再次输入新的登录密码" border="bottom">
38   - <template slot="suffix">
39   - <view @click="showPasswordModeS" style="padding: 10rpx">
40   - <u-icon width="18" height="14" :name="
41   - showPasswordS ? '/static/eye-hide.png' : '/static/eye.png'
42   - "></u-icon>
43   - </view>
44   - </template>
45   - </u-input>
46   - </view>
47   - <button class="submit" size="default" @click="onSubmit"><text style="color:#fff">确定</text></button>
48   - </view>
49   - </view>
50   - </view>
51   -</template>
52   -
53   -<script>
54   - import api from '@/api/index.js'
55   - import { loginPasswordReg } from '@/plugins/utils.js'
56   -
57   - var clear;
58   - export default {
59   - data() {
60   - return {
61   - readonly: false,
62   - codeText: '发送验证码',
63   - phone: '', //号码
64   - vCode: '', //验证码
65   - nextStatus: false,
66   - password: '',
67   - rePassword: '',
68   - showPasswordF: true,
69   - showPasswordS: true
70   - };
71   - },
72   - methods: {
73   - //验证码按钮文字状态
74   - getCodeState() {
75   - const _this = this;
76   - this.readonly = true;
77   - this.codeText = '60s后重新获取';
78   - var s = 60;
79   - clear = setInterval(() => {
80   - s--;
81   - _this.codeText = s + 's后重新获取';
82   - if (s <= 0) {
83   - clearInterval(clear);
84   - _this.codeText = '发送验证码';
85   - _this.readonly = false;
86   - }
87   - }, 1000);
88   - },
89   - //获取验证码
90   - getVcode() {
91   - if (this.readonly) {
92   - uni.showToast({
93   - title: '验证码已发送~',
94   - icon: 'none'
95   - });
96   - return;
97   - }
98   - if (this.phone == '') {
99   - uni.showToast({
100   - title: '请输入手机号~',
101   - icon: 'none'
102   - });
103   - return;
104   - }
105   - const phoneRegular = /^1\d{10}$/;
106   - if (!phoneRegular.test(this.phone)) {
107   - uni.showToast({
108   - title: '手机号格式不正确~',
109   - icon: 'none'
110   - });
111   - return;
112   - }
113   - let httpData = {};
114   - // 获取验证码接口
115   - api.loginApi.postCodeApi(this.phone)
116   - .then(res => {
117   - this.getCodeState(); //开始倒计时
118   - })
119   - .catch(err => {
120   - uni.showToast({
121   - title: err.data.msg,
122   - icon: 'none'
123   - });
124   - });
125   - },
126   - onNextSubmit() {
127   - const phoneRegular = /^1\d{10}$/;
128   - if (this.phone == '') {
129   - uni.showToast({
130   - title: '请输入手机号码~',
131   - icon: 'none'
132   - });
133   - return;
134   - } else if (!phoneRegular.test(this.phone)) {
135   - uni.showToast({
136   - title: '手机号格式不正确~',
137   - icon: 'none'
138   - });
139   - return;
140   - }
141   - if (this.vCode == '') {
142   - uni.showToast({
143   - title: '请输入验证码~',
144   - icon: 'none'
145   - });
146   - return;
147   - } else if (!/^\d{6}$/.test(this.vCode)) {
148   - uni.showToast({
149   - title: '验证码格式不正确~',
150   - icon: 'none'
151   - });
152   - return;
153   - }
154   - this.nextStatus = true;
155   - },
156   - showPhone() {
157   - this.nextStatus = false;
158   - },
159   - onSubmit() {
160   - if (this.password == '' && this.rePassword == '') {
161   - uni.showToast({
162   - title: '请输入密码~',
163   - icon: 'none'
164   - });
165   - return;
166   - } else if (!loginPasswordReg.test(this.password) && !loginPasswordReg.test(this.rePassword)) {
167   - //uni.showToast,字数过长,会造成手机上显示不完全,官方bug,采用uni.showModal
168   - uni.showModal({
169   - title: '提示',
170   - content: '密码格式不正确(至少一个大写英文字母、至少一个小写英文字母、至少一位数字、至少一个特殊字符、最少八个字符)~',
171   - showCancel: false
172   - });
173   - return;
174   - }
175   - if (this.password !== this.rePassword) return uni.$u.toast('两次输入密码不一致');
176   - let httpData = {
177   - password: this.password,
178   - phoneNumber: this.phone,
179   - userId: this.vCode
180   - };
181   - const res = api.loginApi.postResetCodeApi(this.phone, httpData)
182   - if (res) {
183   - uni.showToast({
184   - title: '重置密码成功~',
185   - icon: 'none'
186   - }).then(res => {
187   - uni.reLaunch({
188   - url: '/publicLoginSubPage/public/login'
189   - });
190   - });
191   - }
192   - },
193   - showPasswordMode() {
194   - this.showPasswordF = !this.showPasswordF;
195   - },
196   - showPasswordModeS() {
197   - this.showPasswordS = !this.showPasswordS;
198   - }
199   - }
200   - };
201   -</script>
202   -
203   -<style lang="scss" scoped>
204   - @import './static/findPassword.scss';
205   -</style>
... ... @@ -47,6 +47,63 @@ button {
47 47 white-space: nowrap;
48 48 }
49 49 ///////////////////////////////////////////////////////////////小程序、app抽取公共样式/////////////////////////////////////////////////////////////////////
  50 +.flex-column{
  51 + display: flex;
  52 + flex-direction: column;
  53 +}
  54 +.flex{
  55 + display: flex;
  56 +}
  57 +.justify-center{
  58 + display: flex;
  59 + justify-content: center;
  60 + align-items: center;
  61 +}
  62 +.mt-3{
  63 + margin-top: 30rpx;
  64 +}
  65 +.ml-16{
  66 + margin-left: 16rpx;
  67 +}
  68 +.mt-15{
  69 + margin-top: 15rpx;
  70 +}
  71 +.mt-20{
  72 + margin-top: 20rpx;
  73 +}
  74 +.mr-1{
  75 + margin-right: 10rpx;
  76 +}
  77 +.mr-2{
  78 + margin-right: 20rpx;
  79 +}
  80 +.ml-3{
  81 + margin-top: 20rpx;
  82 +}
  83 +.ml-1{
  84 + margin-left: 10rpx;
  85 +}
  86 +.pl-3{
  87 + padding-left: 30rpx;
  88 +}
  89 +.w-100{
  90 + width: 100%;
  91 +}
  92 +.w-300{
  93 + width:300rpx;
  94 +}
  95 +.w-500{
  96 + width:500rpx;
  97 +}
  98 +.h-25{
  99 + height:25rpx;
  100 +}
  101 +.h-30{
  102 + height:30rpx;
  103 +}
  104 +.h-140{
  105 + height:140rpx;
  106 +}
50 107 //通用(设备、告警、摄像头分页头部组织和设备数和设备、告警里面的详情(左边的文本)
51 108 .ml-10{
52 109 margin-left: 10rpx;
... ...
  1 +const actions = [{
  2 + name: '全部',
  3 + value: ''
  4 + },
  5 + {
  6 + name: '会议',
  7 + value: 'MEETING'
  8 + },
  9 + {
  10 + name: '公告',
  11 + value: 'NOTICE'
  12 + },
  13 + {
  14 + name: '其他',
  15 + value: 'OTHER'
  16 + }
  17 +]
  18 +
  19 +
  20 +export {
  21 + actions
  22 +}
\ No newline at end of file
... ...
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 w-500">
  9 + <text class="notify-detail-text ">{{ notifyList.title }}</text>
  10 + </view>
  11 + </view>
  12 + <view class="h-25"></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 class="mt-20">
  23 + <!-- 富文本解析 -->
  24 + <u-parse :content="notifyList.content"></u-parse>
  25 + </view>
  26 + </view>
  27 + </view>
  28 + </view>
  29 + </view>
  30 +</template>
  31 +
  32 +<script>
  33 +export default {
  34 + data() {
  35 + return {
  36 + notifyList: {}
  37 + };
  38 + },
  39 + onLoad(e) {
  40 + // 隐藏原生的tabbar
  41 + uni.hideTabBar();
  42 + if (e.data !== null) {
  43 + let params = JSON.parse(decodeURIComponent(e.data));
  44 + if (params) this.notifyList = params;
  45 + }
  46 + }
  47 +};
  48 +</script>
  49 +
  50 +<style lang="scss" scoped>
  51 +@import './static/notifyDetail.scss';
  52 +/deep/ .u-line {
  53 + position: relative !important;
  54 + width: 640rpx !important;
  55 + left: 31rpx !important;
  56 +}
  57 +</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 + import { actions } from './config/data.js'
  48 +
  49 + export default {
  50 + mixins: [MescrollMixin],
  51 + data() {
  52 + return {
  53 + notifyType:'',
  54 + showType: false,
  55 + actions,
  56 + page: {
  57 + page: 0,
  58 + pageSize: 10
  59 + },
  60 + downOption: {
  61 + auto: false //是否在初始化后,自动执行downCallback; 默认true
  62 + },
  63 + list: [],
  64 + selectType: '',
  65 + distance: 0,
  66 + };
  67 + },
  68 + onLoad(e) {
  69 + // 隐藏原生的tabbar
  70 + uni.hideTabBar();
  71 + },
  72 + methods: {
  73 + formatType(e) {
  74 + const findName = this.actions.find((item)=>item.value===e && item.value!=='')
  75 + if(!findName) return
  76 + return findName.name
  77 + },
  78 + handleTypeClick() {
  79 + this.showType = true;
  80 + uni.hideKeyboard();
  81 + },
  82 + handleTypeSelect(e) {
  83 + this.selectType = e.value;
  84 + this.page.page = 1;
  85 + this.notifyType = e.name;
  86 + this.loadData(1, !this.selectType ? null : this.selectType);
  87 + },
  88 + downCallback() {
  89 + this.list.length = 0;
  90 + this.page.page = 1;
  91 + this.loadData(1);
  92 + this.selectType = '';
  93 + this.notifyType = '';
  94 + },
  95 + upCallback() {
  96 + if (this.selectType) {
  97 + this.page.page += 1;
  98 + this.loadData(this.page.page, this.selectType);
  99 + } else {
  100 + this.page.page += 1;
  101 + this.loadData(this.page.page);
  102 + }
  103 + },
  104 + async loadData(page, type) {
  105 + let params = {
  106 + page,
  107 + pageSize: 10,
  108 + type
  109 + };
  110 + const res = await api.notifyApi.getNotifyApi({
  111 + params,
  112 + custom: {
  113 + load: false
  114 + }
  115 + })
  116 + uni.stopPullDownRefresh();
  117 + this.mescroll.endByPage(res.items.length, res.total);
  118 + if (page == 1) {
  119 + this.list = res.items;
  120 + } else {
  121 + this.list = this.list.concat(res.items);
  122 + }
  123 + },
  124 + async handleNotifyDetail(item, index) {
  125 + this.list[index].readStatus = 1;//点击了则说明阅读了
  126 + await api.notifyApi.byNotifyIdGetDetailApi(item.sysNotice.id)
  127 + useNavigateTo('./notify-detail?data=', item.sysNotice)
  128 + }
  129 + }
  130 + };
  131 +</script>
  132 +
  133 +<style lang="scss" scoped>
  134 + @import './static/systemNotify.scss';
  135 +
  136 + /deep/ .u-badge--error {
  137 + position: relative;
  138 + right: 11rpx;
  139 + }
  140 +</style>
... ...