Commit 911695fda38a22b8d2ff8cdac6faff5c959a8e0f
Merge branch 'feat/add-device-list-command-issuance' into 'main_dev'
feat: 设备命令下发添加服务调用 See merge request yunteng/thingskit-app!154
Showing
5 changed files
with
1048 additions
and
384 deletions
... | ... | @@ -4,37 +4,47 @@ |
4 | 4 | * data ((deviceProfileIds)) |
5 | 5 | */ |
6 | 6 | const getDeviceApi = (urlParams, data) => { |
7 | - const { | |
8 | - page, | |
9 | - pageSize, | |
10 | - } = urlParams | |
11 | - return uni.$u.http.post(`/yt/device?page=${page}&pageSize=${pageSize}`, data); | |
12 | -}; | |
7 | + const { page, pageSize } = urlParams | |
8 | + return uni.$u.http.post(`/yt/device?page=${page}&pageSize=${pageSize}`, data) | |
9 | +} | |
13 | 10 | |
14 | 11 | // 设备详情 |
15 | 12 | const getDeviceDetail = (id) => { |
16 | - return uni.$u.http.get(`/yt/device/${id}`); | |
17 | -}; | |
13 | + return uni.$u.http.get(`/yt/device/${id}`) | |
14 | +} | |
18 | 15 | |
19 | 16 | //设备属性 |
20 | 17 | const getAttribute = (deviceProfileId) => { |
21 | - return uni.$u.http.get(`/yt/device/attributes/${deviceProfileId}`); | |
22 | -}; | |
18 | + return uni.$u.http.get(`/yt/device/attributes/${deviceProfileId}`) | |
19 | +} | |
23 | 20 | |
24 | 21 | //命令下发 |
25 | 22 | const issueCommand = (type, tbDeviceId, data) => { |
26 | - return uni.$u.http.post(`/rpc/${type==='OneWay'?'oneway':'twoway'}/${tbDeviceId}`, data) | |
23 | + return uni.$u.http.post(`/rpc/${type === 'OneWay' ? 'oneway' : 'twoway'}/${tbDeviceId}`, data) | |
27 | 24 | } |
28 | 25 | |
29 | 26 | //获取命令下发记录 |
30 | 27 | const getRpcRecord = (params) => { |
31 | - return uni.$u.http.get('/yt/rpc', params); | |
32 | -}; | |
28 | + return uni.$u.http.get('/yt/rpc', params) | |
29 | +} | |
30 | + | |
31 | +// 获取设备状态 在线or离线 | |
32 | +const getDeviceActiveTime = (entityId) => { | |
33 | + return uni.$u.http.get(`/plugins/telemetry/DEVICE/${entityId}/values/attributes?keys=active`) | |
34 | +} | |
35 | + | |
36 | +// 获取服务调用 | |
37 | +const getModelServices = (params) => { | |
38 | + const { deviceProfileId } = params | |
39 | + return uni.$u.http.get(`/yt/things_model/get_services/${deviceProfileId}`) | |
40 | +} | |
33 | 41 | |
34 | 42 | export default { |
35 | - getDeviceApi, | |
36 | - getDeviceDetail, | |
37 | - getAttribute, | |
38 | - issueCommand, | |
39 | - getRpcRecord | |
40 | -} | |
\ No newline at end of file | ||
43 | + getDeviceApi, | |
44 | + getDeviceDetail, | |
45 | + getAttribute, | |
46 | + issueCommand, | |
47 | + getRpcRecord, | |
48 | + getModelServices, | |
49 | + getDeviceActiveTime, | |
50 | +} | ... | ... |
... | ... | @@ -6,8 +6,7 @@ |
6 | 6 | <view class="basic-header"> |
7 | 7 | <view class="u-flex"> |
8 | 8 | <view class="pl-3"> |
9 | - <u-icon v-if="deviceDetail.deviceInfo.longitude !== ''" @click="handleClick" | |
10 | - name="map-fill"></u-icon> | |
9 | + <u-icon v-if="deviceDetail.deviceInfo.longitude !== ''" @click="handleClick" name="map-fill"></u-icon> | |
11 | 10 | </view> |
12 | 11 | <view class="basic-text text-clip ml-2"> |
13 | 12 | {{ deviceDetail.alias ? deviceDetail.alias : deviceDetail.name }} |
... | ... | @@ -17,13 +16,13 @@ |
17 | 16 | </view> |
18 | 17 | </view> |
19 | 18 | <!-- 命令下发 设备在线并且不是网关子设备 --> |
20 | - <view class="mr-2" v-if="deviceDetail.deviceState === 'ONLINE' && deviceDetail.transportType!==deviceTypeNum.GBT"> | |
19 | + <view class="mr-2" v-if="deviceDetail.deviceState === 'ONLINE' && deviceDetail.transportType !== deviceTypeNum.GBT"> | |
21 | 20 | <!-- #ifdef MP --> |
22 | - <u-button type="primary" shape="circle" size="mini" text="下发命令" @click="handleMpShowModal" /> | |
21 | + <u-button type="primary" shape="circle" size="mini" text="命令下发" @click="handleMpShowModal" /> | |
23 | 22 | <!-- #endif --> |
24 | 23 | <!-- #ifdef APP-PLUS --> |
25 | 24 | <view class="cu-item" @tap="handleAppShowModal" data-target="Modal"> |
26 | - <text>下发命令</text> | |
25 | + <text>命令下发</text> | |
27 | 26 | </view> |
28 | 27 | <!-- #endif --> |
29 | 28 | </view> |
... | ... | @@ -79,7 +78,7 @@ |
79 | 78 | <radio :value="item.value" :checked="index === current" /> |
80 | 79 | </view> |
81 | 80 | <view style="width:10rpx"></view> |
82 | - <view class="ml-1">{{item.name}}</view> | |
81 | + <view class="ml-1">{{ item.name }}</view> | |
83 | 82 | </view> |
84 | 83 | </label> |
85 | 84 | </radio-group> |
... | ... | @@ -87,9 +86,9 @@ |
87 | 86 | </view> |
88 | 87 | <view class="app-command-body"> |
89 | 88 | <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"> | |
89 | + :placeholder="`请输入下发内容${isShowTCP ? '(字符串格式)' : '(json格式)'}`" /> | |
90 | + <u-icon @click="handleCopy(copyTextValue)" v-if="!isShowTCP" name="question-circle" color="#2979ff" | |
91 | + size="28" class="ml-10"> | |
93 | 92 | </u-icon> |
94 | 93 | </view> |
95 | 94 | <view class="app-command-buttons"> |
... | ... | @@ -104,315 +103,326 @@ |
104 | 103 | <!-- #ifdef MP --> |
105 | 104 | <!-- u-modal在app端弹窗层级无法覆盖背景色 --> |
106 | 105 | <mp-command-issuance ref="mpCommandIssuanceRef" :isShowTCP="isShowTCP" :showModal="mpShowModal" |
107 | - @hideModal="hideMpModal" @cancelCommand="cancelCommand" | |
106 | + :deviceDetail="deviceDetail" @hideModal="hideMpModal" @cancelCommand="cancelCommand" | |
108 | 107 | @confirmCommand="confirmCommand"></mp-command-issuance> |
109 | 108 | <!-- #endif --> |
110 | 109 | </view> |
111 | 110 | </template> |
112 | 111 | |
113 | 112 | <script> |
114 | - import {formatToDate} from '@/plugins/utils.js'; | |
115 | - import api from '@/api/index.js'; | |
116 | - import mpCommandIssuance from './mp-command-issuance.vue'; | |
117 | - import {commandTypeList} from '../config/data.js' | |
118 | - import {useShowModal} from '@/plugins/utils.js' | |
119 | - import {deviceTypeNum} from '../config/data' | |
120 | - | |
121 | - export default { | |
122 | - components: { | |
123 | - mpCommandIssuance, | |
124 | - }, | |
125 | - props: { | |
126 | - deviceDetail: { | |
127 | - type: Object, | |
128 | - default: () => ({}) | |
113 | +import { formatToDate } from '@/plugins/utils.js'; | |
114 | +import api from '@/api/index.js'; | |
115 | +import mpCommandIssuance from './mp-command-issuance.vue'; | |
116 | +import { commandTypeList } from '../config/data.js' | |
117 | +import { useShowModal } from '@/plugins/utils.js' | |
118 | +import { deviceTypeNum } from '../config/data' | |
119 | +import deviceDetail from '../device-detail.vue'; | |
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 | + deviceTypeNum: deviceTypeNum, | |
137 | + commandTypeStr: 'OneWay', | |
138 | + inputCommandContent: '', | |
139 | + mpShowModal: false, | |
140 | + commandValue: {}, | |
141 | + isShowTCP: false, //用于下发命令时判断是否是TCP/UDP | |
142 | + modalName: null, | |
143 | + copyTextValue: { | |
144 | + "method": "methodThingskit", | |
145 | + "params": { | |
146 | + "pin": 7, | |
147 | + "value": 1 | |
148 | + } | |
129 | 149 | } |
150 | + }; | |
151 | + }, | |
152 | + computed: { | |
153 | + deviceStatus() { | |
154 | + return this.deviceDetail.deviceState === 'INACTIVE' ? '待激活' : this.deviceDetail.deviceState === 'ONLINE' ? | |
155 | + '在线' : '离线'; | |
130 | 156 | }, |
131 | - data() { | |
132 | - return { | |
133 | - showNativeModal: false, | |
134 | - current: 0, | |
135 | - commandTypeList, | |
136 | - deviceTypeNum:deviceTypeNum, | |
137 | - commandTypeStr: 'OneWay', | |
138 | - inputCommandContent: '', | |
139 | - mpShowModal: false, | |
140 | - commandValue: {}, | |
141 | - isShowTCP: false, //用于下发命令时判断是否是TCP/UDP | |
142 | - modalName: null, | |
143 | - copyTextValue: { | |
144 | - "method": "methodThingskit", | |
145 | - "params": { | |
146 | - "pin": 7, | |
147 | - "value": 1 | |
157 | + deviceType() { | |
158 | + return this.deviceDetail.deviceType === 'DIRECT_CONNECTION' ? | |
159 | + '直连设备' : | |
160 | + this.deviceDetail.deviceType === 'GATEWAY' ? | |
161 | + '网关设备' : | |
162 | + this.deviceDetail.deviceType === 'SENSOR' ? | |
163 | + '网关子设备' : | |
164 | + ''; | |
165 | + }, | |
166 | + alarmStatus() { | |
167 | + return this.deviceDetail.alarmStatus === '0' ? '否' : '是'; | |
168 | + }, | |
169 | + formatLastOnlineTime() { | |
170 | + return formatToDate(Number(this.deviceDetail.lastOnlineTime), 'YYYY-MM-DD HH:mm:ss'); | |
171 | + } | |
172 | + }, | |
173 | + beforeCreate() { | |
174 | + this.modalName = null | |
175 | + }, | |
176 | + onLoad() { | |
177 | + // 隐藏原生的tabbar | |
178 | + uni.hideTabBar(); | |
179 | + this.modalName = null | |
180 | + }, | |
181 | + methods: { | |
182 | + handleCopy(value) { | |
183 | + useShowModal(JSON.stringify(value), '命令下发', '复制内容').then(res => { | |
184 | + uni.setClipboardData({ | |
185 | + data: JSON.stringify(value), | |
186 | + success: () => { | |
187 | + uni.showToast({ | |
188 | + title: '复制成功' | |
189 | + }) | |
148 | 190 | } |
191 | + }); | |
192 | + }) | |
193 | + }, | |
194 | + radioChange: function (evt) { | |
195 | + for (let i = 0; i < this.commandTypeList.length; i++) { | |
196 | + if (this.items[i].value === evt.detail.value) { | |
197 | + this.current = i; | |
198 | + break; | |
149 | 199 | } |
200 | + } | |
201 | + this.commandTypeStr = evt.detail.value | |
202 | + }, | |
203 | + formatTextStatus(deviceState) { | |
204 | + return deviceState === 'INACTIVE' ? '#666' : deviceState === 'ONLINE' ? '#377DFF' : '#DE4437'; | |
205 | + }, | |
206 | + handleClick() { | |
207 | + const data = { | |
208 | + longitude: this.deviceDetail.deviceInfo.longitude || 0, | |
209 | + latitude: this.deviceDetail.deviceInfo.latitude || 0 | |
150 | 210 | }; |
211 | + uni.navigateTo({ | |
212 | + url: '/device-subpackage/device-detail/device-position?data=' + JSON.stringify(data) | |
213 | + }); | |
151 | 214 | }, |
152 | - computed: { | |
153 | - deviceStatus() { | |
154 | - return this.deviceDetail.deviceState === 'INACTIVE' ? '待激活' : this.deviceDetail.deviceState === 'ONLINE' ? | |
155 | - '在线' : '离线'; | |
156 | - }, | |
157 | - deviceType() { | |
158 | - return this.deviceDetail.deviceType === 'DIRECT_CONNECTION' ? | |
159 | - '直连设备' : | |
160 | - this.deviceDetail.deviceType === 'GATEWAY' ? | |
161 | - '网关设备' : | |
162 | - this.deviceDetail.deviceType === 'SENSOR' ? | |
163 | - '网关子设备' : | |
164 | - ''; | |
165 | - }, | |
166 | - alarmStatus() { | |
167 | - return this.deviceDetail.alarmStatus === '0' ? '否' : '是'; | |
168 | - }, | |
169 | - formatLastOnlineTime() { | |
170 | - return formatToDate(Number(this.deviceDetail.lastOnlineTime), 'YYYY-MM-DD HH:mm:ss'); | |
171 | - } | |
215 | + disabledScroll() { | |
216 | + return; | |
172 | 217 | }, |
173 | - beforeCreate() { | |
174 | - this.modalName = null | |
218 | + handleAppShowModal(e) { | |
219 | + this.modalName = e.currentTarget.dataset.target; | |
220 | + this.showNativeModal = true | |
175 | 221 | }, |
176 | - onLoad() { | |
177 | - // 隐藏原生的tabbar | |
178 | - uni.hideTabBar(); | |
179 | - this.modalName = null | |
222 | + handleMpShowModal() { | |
223 | + const { | |
224 | + transportType | |
225 | + } = this.deviceDetail.deviceProfile; | |
226 | + this.isShowTCP = transportType == 'TCP' ? true : false; | |
227 | + this.mpShowModal = true; | |
180 | 228 | }, |
181 | - methods: { | |
182 | - handleCopy(value) { | |
183 | - useShowModal(JSON.stringify(value), '命令下发', '复制内容').then(res => { | |
184 | - uni.setClipboardData({ | |
185 | - data: JSON.stringify(value), | |
186 | - success: () => { | |
187 | - uni.showToast({ | |
188 | - title: '复制成功' | |
189 | - }) | |
190 | - } | |
191 | - }); | |
192 | - }) | |
193 | - }, | |
194 | - radioChange: function(evt) { | |
195 | - for (let i = 0; i < this.commandTypeList.length; i++) { | |
196 | - if (this.items[i].value === evt.detail.value) { | |
197 | - this.current = i; | |
198 | - break; | |
199 | - } | |
229 | + hideMpModal() { | |
230 | + this.mpShowModal = false; | |
231 | + }, | |
232 | + hideAppModal() { | |
233 | + this.modalName = null; | |
234 | + this.showNativeModal = false | |
235 | + }, | |
236 | + confirmCommand(commandType, callType, values) { | |
237 | + this.handleCommand(commandType, callType, values) | |
238 | + }, | |
239 | + cancelCommand() { | |
240 | + this.hideMpModal(); | |
241 | + this.hideAppModal(); | |
242 | + this.commandTypeStr = 'OneWay' | |
243 | + this.inputCommandContent = '' | |
244 | + this.$nextTick(() => { | |
245 | + this.$refs.mpCommandIssuanceRef.reset() | |
246 | + }) | |
247 | + }, | |
248 | + handleAppCommand() { | |
249 | + this.handleCommand(this.commandTypeStr, this.inputCommandContent) | |
250 | + }, | |
251 | + async handleCommand(commandType, callType, values) { | |
252 | + if (!values) return uni.$u.toast('请输入下发内容~'); | |
253 | + if(callType=='TwoWay'){ | |
254 | + const result = await api.deviceApi.getDeviceActiveTime(this.deviceDetail.tbDeviceId) | |
255 | + const [firsetItem] = result || [] | |
256 | + if (!firsetItem.value) { | |
257 | + return uni.$u.toast('当前设备不在线~') | |
200 | 258 | } |
201 | - this.commandTypeStr = evt.detail.value | |
202 | - }, | |
203 | - formatTextStatus(deviceState) { | |
204 | - return deviceState === 'INACTIVE' ? '#666' : deviceState === 'ONLINE' ? '#377DFF' : '#DE4437'; | |
205 | - }, | |
206 | - handleClick() { | |
207 | - const data = { | |
208 | - longitude: this.deviceDetail.deviceInfo.longitude || 0, | |
209 | - latitude: this.deviceDetail.deviceInfo.latitude || 0 | |
210 | - }; | |
211 | - uni.navigateTo({ | |
212 | - url: '/device-subpackage/device-detail/device-position?data=' + JSON.stringify(data) | |
213 | - }); | |
214 | - }, | |
215 | - disabledScroll() { | |
216 | - return; | |
217 | - }, | |
218 | - handleAppShowModal(e) { | |
219 | - this.modalName = e.currentTarget.dataset.target; | |
220 | - this.showNativeModal = true | |
221 | - }, | |
222 | - handleMpShowModal() { | |
223 | - const { | |
224 | - transportType | |
225 | - } = this.deviceDetail.deviceProfile; | |
226 | - this.isShowTCP = transportType == 'TCP' ? true : false; | |
227 | - this.mpShowModal = true; | |
228 | - }, | |
229 | - hideMpModal() { | |
230 | - this.mpShowModal = false; | |
231 | - }, | |
232 | - hideAppModal() { | |
233 | - this.modalName = null; | |
234 | - this.showNativeModal = false | |
235 | - }, | |
236 | - confirmCommand(commandType, inputCommandVal) { | |
237 | - this.handleCommand(commandType, inputCommandVal) | |
238 | - }, | |
239 | - cancelCommand() { | |
240 | - this.hideMpModal(); | |
241 | - this.hideAppModal(); | |
242 | - this.commandTypeStr = 'OneWay' | |
243 | - this.inputCommandContent = '' | |
244 | - this.$nextTick(() => { | |
245 | - this.$refs.mpCommandIssuanceRef.reset() | |
246 | - }) | |
247 | - }, | |
248 | - handleAppCommand() { | |
249 | - this.handleCommand(this.commandTypeStr, this.inputCommandContent) | |
250 | - }, | |
251 | - async handleCommand(commandType, inputCommandVal) { | |
252 | - if (!inputCommandVal) return uni.$u.toast('请输入下发内容~'); | |
259 | + } | |
260 | + | |
261 | + this.commandValue.persistent = true; | |
262 | + this.commandValue.additionalInfo = { | |
263 | + cmdType: commandType == 0 ? 0 : 1 | |
264 | + }; | |
265 | + this.commandValue.method = 'methodThingskit'; | |
266 | + this.commandValue.params = values; | |
267 | + if (commandType == 0) {//下发类型是自定义时 | |
253 | 268 | if (this.isShowTCP) { |
254 | 269 | //TCP的格式只能是字符串 |
255 | 270 | const zg = /^[0-9a-zA-Z]*$/; |
256 | - if (!zg.test(inputCommandVal)) { | |
271 | + if (!zg.test(values)) { | |
257 | 272 | uni.$u.toast('输入的内容只能是字母和数字的组合'); |
258 | 273 | return; |
259 | 274 | } |
260 | - this.commandValue.params = inputCommandVal; | |
275 | + this.commandValue.params = values; | |
261 | 276 | } else { |
262 | - this.commandValue.params = JSON.parse(inputCommandVal); | |
277 | + this.commandValue.params = JSON.parse(values); | |
263 | 278 | } |
264 | - this.commandValue.persistent = true; | |
265 | - this.commandValue.additionalInfo = { | |
266 | - cmdType: 'API' | |
267 | - }; | |
268 | - this.commandValue.method = 'methodThingskit'; | |
269 | - this.commandValue.params = inputCommandVal; | |
270 | - await api.deviceApi.issueCommand(commandType, this.deviceDetail.tbDeviceId, this.commandValue); | |
271 | - this.cancelCommand(); | |
272 | - uni.$u.toast('下发成功~'); | |
273 | - | |
274 | - }, | |
279 | + } | |
280 | + | |
281 | + await api.deviceApi.issueCommand(callType, this.deviceDetail.tbDeviceId, this.commandValue); | |
282 | + this.cancelCommand(); | |
283 | + uni.$u.toast('下发成功~'); | |
284 | + }, | |
275 | 285 | |
276 | - } | |
277 | - }; | |
286 | + } | |
287 | +}; | |
278 | 288 | </script> |
279 | 289 | |
280 | 290 | <style lang="scss" scoped> |
281 | - @import url('../static/modal.css'); | |
291 | +@import url('../static/modal.css'); | |
282 | 292 | |
283 | - .app-command-content { | |
284 | - .app-command-text { | |
285 | - display: flex; | |
286 | - justify-content: center; | |
287 | - align-items: center; | |
293 | +.app-command-content { | |
294 | + .app-command-text { | |
295 | + display: flex; | |
296 | + justify-content: center; | |
297 | + align-items: center; | |
288 | 298 | |
289 | - text { | |
290 | - font-weight: 700; | |
291 | - } | |
299 | + text { | |
300 | + font-weight: 700; | |
292 | 301 | } |
302 | + } | |
293 | 303 | |
294 | - .app-command-type { | |
295 | - display: flex; | |
296 | - margin-top: 20rpx; | |
297 | - margin-left: 20rpx; | |
304 | + .app-command-type { | |
305 | + display: flex; | |
306 | + margin-top: 20rpx; | |
307 | + margin-left: 20rpx; | |
298 | 308 | |
299 | - text { | |
300 | - font-weight: 700; | |
301 | - } | |
309 | + text { | |
310 | + font-weight: 700; | |
302 | 311 | } |
312 | + } | |
303 | 313 | |
304 | - .app-command-body { | |
305 | - display: flex; | |
306 | - align-items: center; | |
307 | - justify-content: space-between; | |
308 | - margin-top: 20rpx; | |
309 | - margin-left: 20rpx; | |
314 | + .app-command-body { | |
315 | + display: flex; | |
316 | + align-items: center; | |
317 | + justify-content: space-between; | |
318 | + margin-top: 20rpx; | |
319 | + margin-left: 20rpx; | |
310 | 320 | |
311 | - .app-command-textarea { | |
312 | - width: 625rpx; | |
313 | - height: 400rpx; | |
314 | - background: #FFFFFF; | |
315 | - box-shadow: 2px 2px 4px 0px rgba(0, 0, 0, 0.03); | |
316 | - border-radius: 10px; | |
317 | - } | |
321 | + .app-command-textarea { | |
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; | |
318 | 327 | } |
328 | + } | |
319 | 329 | |
320 | - .app-command-buttons { | |
321 | - display: flex; | |
322 | - align-items: center; | |
323 | - justify-content: space-evenly; | |
324 | - height: 85rpx; | |
325 | - margin-top: 20rpx; | |
326 | - margin-bottom: 20rpx; | |
330 | + .app-command-buttons { | |
331 | + display: flex; | |
332 | + align-items: center; | |
333 | + justify-content: space-evenly; | |
334 | + height: 85rpx; | |
335 | + margin-top: 20rpx; | |
336 | + margin-bottom: 20rpx; | |
327 | 337 | |
328 | - .cancel-button { | |
329 | - width: 300rpx; | |
330 | - background: #e3e3e5; | |
331 | - height: 85rpx; | |
332 | - border-radius: 38rpx; | |
333 | - line-height: 85rpx; | |
338 | + .cancel-button { | |
339 | + width: 300rpx; | |
340 | + background: #e3e3e5; | |
341 | + height: 85rpx; | |
342 | + border-radius: 38rpx; | |
343 | + line-height: 85rpx; | |
334 | 344 | |
335 | - .cancel-text { | |
336 | - color: #333333 | |
337 | - } | |
345 | + .cancel-text { | |
346 | + color: #333333 | |
338 | 347 | } |
348 | + } | |
339 | 349 | |
340 | - .confrim-button { | |
341 | - width: 300rpx; | |
342 | - background: #3388ff; | |
343 | - border-radius: 38rpx; | |
344 | - height: 85rpx; | |
345 | - line-height: 85rpx; | |
350 | + .confrim-button { | |
351 | + width: 300rpx; | |
352 | + background: #3388ff; | |
353 | + border-radius: 38rpx; | |
354 | + height: 85rpx; | |
355 | + line-height: 85rpx; | |
346 | 356 | |
347 | - .confrim-text { | |
348 | - color: white | |
349 | - } | |
357 | + .confrim-text { | |
358 | + color: white | |
350 | 359 | } |
351 | 360 | } |
352 | 361 | } |
362 | +} | |
353 | 363 | |
354 | - .basic-page { | |
355 | - padding: 0 30rpx; | |
364 | +.basic-page { | |
365 | + padding: 0 30rpx; | |
356 | 366 | |
357 | - .basic-header { | |
358 | - display: flex; | |
359 | - justify-content: space-between; | |
360 | - align-items: center; | |
361 | - height: 140rpx; | |
362 | - background-color: #fff; | |
363 | - border-radius: 20rpx; | |
367 | + .basic-header { | |
368 | + display: flex; | |
369 | + justify-content: space-between; | |
370 | + align-items: center; | |
371 | + height: 140rpx; | |
372 | + background-color: #fff; | |
373 | + border-radius: 20rpx; | |
364 | 374 | |
365 | - .basic-text { | |
366 | - width: 370rpx; | |
367 | - } | |
375 | + .basic-text { | |
376 | + width: 370rpx; | |
377 | + } | |
368 | 378 | |
369 | - .cu-item { | |
370 | - background: #3388ff; | |
371 | - border-radius: 12px; | |
372 | - width: 120rpx; | |
373 | - height: 48rpx; | |
374 | - text-align: center; | |
375 | - line-height: 40rpx; | |
379 | + .cu-item { | |
380 | + background: #3388ff; | |
381 | + border-radius: 12px; | |
382 | + width: 120rpx; | |
383 | + height: 48rpx; | |
384 | + text-align: center; | |
385 | + line-height: 40rpx; | |
376 | 386 | |
377 | - text { | |
378 | - font-size: 12px; | |
379 | - font-family: PingFangSC-Regular, PingFang SC; | |
380 | - font-weight: 400; | |
381 | - color: #ffffff; | |
382 | - } | |
387 | + text { | |
388 | + font-size: 12px; | |
389 | + font-family: PingFangSC-Regular, PingFang SC; | |
390 | + font-weight: 400; | |
391 | + color: #ffffff; | |
383 | 392 | } |
393 | + } | |
384 | 394 | |
385 | - .basic-text-status { | |
386 | - font-size: 14px; | |
387 | - } | |
395 | + .basic-text-status { | |
396 | + font-size: 14px; | |
388 | 397 | } |
398 | + } | |
389 | 399 | |
390 | - .detail { | |
391 | - background-color: #fff; | |
392 | - margin-top: 30rpx; | |
393 | - border-radius: 20rpx; | |
394 | - width: 690rpx; | |
400 | + .detail { | |
401 | + background-color: #fff; | |
402 | + margin-top: 30rpx; | |
403 | + border-radius: 20rpx; | |
404 | + width: 690rpx; | |
395 | 405 | |
396 | - .detail-item { | |
397 | - padding: 30rpx; | |
398 | - display: flex; | |
399 | - align-items: center; | |
406 | + .detail-item { | |
407 | + padding: 30rpx; | |
408 | + display: flex; | |
409 | + align-items: center; | |
400 | 410 | |
401 | - .detail-label { | |
402 | - color: #333; | |
403 | - font-size: 15px; | |
404 | - } | |
411 | + .detail-label { | |
412 | + color: #333; | |
413 | + font-size: 15px; | |
414 | + } | |
405 | 415 | |
406 | - .detail-value { | |
407 | - color: #666; | |
408 | - font-size: 14px; | |
409 | - margin-left: 30rpx; | |
410 | - } | |
416 | + .detail-value { | |
417 | + color: #666; | |
418 | + font-size: 14px; | |
419 | + margin-left: 30rpx; | |
411 | 420 | } |
412 | 421 | } |
413 | 422 | } |
423 | +} | |
414 | 424 | |
415 | - /deep/ .u-modal__content { | |
416 | - padding: 30rpx 0 !important; | |
417 | - } | |
425 | +/deep/ .u-modal__content { | |
426 | + padding: 30rpx 0 !important; | |
427 | +} | |
418 | 428 | </style> | ... | ... |
1 | 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> | |
2 | + <view class="mp-u-modal"> | |
3 | + <u-modal :mask-close-able="true" :show="showModal" closeOnClickOverlay :showConfirmButton="false" @close="$emit('hideModal')" z-index="99999"> | |
4 | + <view class="w-100 modal-content"> | |
5 | + <view style="max-height:560rpx;overflow-y: scroll;"> | |
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" @change="handleCommand"> | |
10 | + <u-radio :customStyle="{marginRight: '20rpx'}" v-for="item in commandTypeList" activeColor="#3388FF" :label="item.label" :name="item.value" :key="item.value"></u-radio> | |
11 | + </u-radio-group> | |
12 | + </view> | |
13 | + <view class="u-flex" style="margin-top: 28rpx" v-if="commandType == 0"> | |
14 | + <text class="type-text">单向/双向:</text> | |
15 | + <u-radio-group v-model="callType" placement="row"> | |
16 | + <u-radio activeColor="#3388FF" label="单向" name="OneWay"></u-radio> | |
17 | + <view style="margin: 0 20rpx"></view> | |
18 | + <u-radio activeColor="#3388FF" label="双向" name="TwoWay"></u-radio> | |
19 | + </u-radio-group> | |
20 | + </view> | |
21 | + <view class="u-flex" style="margin-top: 28rpx" v-else> | |
22 | + <text class="type-text">服务:</text> | |
23 | + <view @click="openService"> | |
24 | + <u-input shape="circle" v-model="serviceName" placeholder="请选择服务" disabled disabledColor="#fff" suffixIcon="arrow-down" /> | |
25 | + </view> | |
26 | + </view> | |
27 | + <view class="u-flex" style="margin-top: 28rpx; flex-direction: column; align-items: flex-start" v-if="isShowServiceFunctionName && commandType == 1"> | |
28 | + <text class="type-text">输入参数:</text> | |
29 | + <seriesForm ref="seriesFormRef" :seriesInputData="seriesInputData" :isTCPTransport="isTCPTransport"></seriesForm> | |
30 | + </view> | |
31 | + <view class="content-body" v-if="commandType == 0"> | |
32 | + <div class="u-flex u-row-between"> | |
33 | + <u--textarea :placeholder="`请输入下发内容${isShowTCP ? '(字符串格式)' : '(json格式)'}`" v-model="inputCommandVal" /> | |
34 | + <u-icon v-if="!isShowTCP" @click="handleCopy(copyTextValue)" name="question-circle" color="#2979ff" size="28" class="ml-10"> </u-icon> | |
35 | + </div> | |
36 | + </view> | |
37 | + </view> | |
38 | + <view class="button-group"> | |
39 | + <view> | |
40 | + <u-button :customStyle="{ color: '#333' }" color="#e3e3e5" shape="circle" text="取消" @click="cancelCommand"></u-button> | |
41 | + </view> | |
42 | + <view> | |
43 | + <u-button color="#3388ff" shape="circle" text="确认" @click="confirmCommand"></u-button> | |
44 | + </view> | |
45 | + </view> | |
46 | + </view> | |
47 | + <u-picker | |
48 | + :show="isShowService" | |
49 | + :columns="[seriesList.map((item) => ({ label: item.functionName, value: item.identifier,callType:item.callType }))]" | |
50 | + keyName="label" | |
51 | + closeOnClickOverlay | |
52 | + @cancel="cancelTypeGap" | |
53 | + @close="cancelTypeGap" | |
54 | + @confirm="handleSelect" | |
55 | + ></u-picker> | |
56 | + </u-modal> | |
57 | + </view> | |
36 | 58 | </template> |
37 | 59 | |
38 | 60 | <script> |
39 | - import { | |
40 | - useShowModal | |
41 | - } from '@/plugins/utils.js' | |
61 | +import { useShowModal } from '@/plugins/utils.js' | |
62 | +import seriesForm from './seriesForm.vue' | |
42 | 63 | |
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 | - } | |
64 | +import api from '@/api/index.js' | |
65 | + | |
66 | +export default { | |
67 | + components: { | |
68 | + seriesForm, | |
69 | + }, | |
70 | + props: { | |
71 | + showModal: Boolean, | |
72 | + isShowTCP: Boolean, | |
73 | + deviceDetail: Object, | |
74 | + }, | |
75 | + data() { | |
76 | + return { | |
77 | + current: 0, | |
78 | + commandType: 0, //下发类型 | |
79 | + callType: 'OneWay', //单双向 | |
80 | + serviceName: '', //服务 | |
81 | + service: null, //服务 | |
82 | + inputCommandVal: '',//自定义命令 | |
83 | + isShowService: false, //服务下拉框 | |
84 | + isShowServiceFunctionName: false, //选择服务过后的输入参数 | |
85 | + copyTextValue: { | |
86 | + method: 'methodThingskit', | |
87 | + params: { | |
88 | + pin: 7, | |
89 | + value: 1, | |
90 | + }, | |
91 | + }, | |
92 | + commandTypeList: [{ label: '自定义', value: 0 }], | |
93 | + seriesList: [], //服务下拉框的数据 | |
94 | + boolList: [], | |
95 | + enumList: [], | |
96 | + seriesInputData: [], | |
97 | + isTCPTransport:false | |
98 | + } | |
99 | + }, | |
100 | + mounted() { | |
101 | + this.getFormInfo() | |
102 | + }, | |
103 | + watch: { | |
104 | + showModal: { | |
105 | + deep: true, | |
106 | + handler() { | |
107 | + this.commandType = 0 | |
108 | + this.serviceName = '' | |
109 | + this.isShowServiceFunctionName = false | |
110 | + }, | |
111 | + }, | |
112 | + }, | |
113 | + methods: { | |
114 | + cancelCommand() { | |
115 | + this.commandType = 0 | |
116 | + this.$emit('cancelCommand') | |
117 | + }, | |
118 | + async confirmCommand() { | |
119 | + if(this.commandType==0){ | |
120 | + this.$emit('confirmCommand',this.commandType, this.callType, this.inputCommandVal) | |
121 | + }else{ | |
122 | + const result = this.$refs.seriesFormRef.handleValidate() | |
123 | + if(!result){ | |
124 | + return | |
125 | + } | |
126 | + const value = this.$refs.seriesFormRef.getFormField() | |
127 | + const values =this.isTCPTransport?value.serviceCommand: { | |
128 | + [this.service]:value | |
59 | 129 | } |
130 | + this.$emit('confirmCommand',this.commandType, this.callType, values) | |
60 | 131 | } |
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 = '' | |
132 | + }, | |
133 | + handleCopy(value) { | |
134 | + useShowModal(JSON.stringify(value), '命令下发', '复制内容').then((res) => { | |
135 | + uni.setClipboardData({ | |
136 | + data: JSON.stringify(value), | |
137 | + success: () => { | |
138 | + uni.showToast({ | |
139 | + title: '复制成功', | |
140 | + }) | |
141 | + }, | |
142 | + }) | |
143 | + }) | |
144 | + }, | |
145 | + | |
146 | + async getFormInfo() { | |
147 | + const { transportType, deviceType, deviceProfile } = this.deviceDetail || {} | |
148 | + const { | |
149 | + profileData: { | |
150 | + transportConfiguration: { protocol }, | |
151 | + }, | |
152 | + } = deviceProfile || {} | |
153 | + this.isTCPTransport = transportType === 'TCP' | |
154 | + const isTCPModbus = this.isTCPTransport && protocol === 'MODBUS_RTU' | |
155 | + if (isTCPModbus || (this.isTCPTransport && deviceType === 'SENSOR')) { | |
156 | + this.commandTypeList = this.commandTypeList.length==2?this.commandTypeList.pop():this.commandTypeList | |
157 | + } else { | |
158 | + this.commandTypeList.push({ label: '服务', value: 1 }) | |
159 | + this.seriesList = await api.deviceApi.getModelServices(this.deviceDetail) | |
160 | + } | |
161 | + }, | |
162 | + | |
163 | + openService() { | |
164 | + this.isShowService = true | |
165 | + }, | |
166 | + handleSelect(e) { | |
167 | + this.isShowService = false | |
168 | + const { value } = e || {} | |
169 | + this.serviceName = value[0].label | |
170 | + this.service = value[0].value | |
171 | + if (this.service) { | |
172 | + this.isShowServiceFunctionName = true | |
173 | + const { functionJson } = this.seriesList.filter((item) => item.identifier === this.service)[0] || {} | |
174 | + const { inputData } = functionJson || {} | |
175 | + this.seriesInputData = inputData || [] | |
176 | + this.callType = value[0].callType==='ASYNC'?'OneWay':'TwoWay' | |
177 | + } | |
178 | + }, | |
179 | + | |
180 | + cancelModel() { | |
181 | + this.isShowService = false | |
182 | + this.seriesInputData = [] | |
183 | + this.seriesList = [] | |
184 | + }, | |
185 | + | |
186 | + handleCommand(name){ | |
187 | + this.seriesInputData = [] | |
188 | + this.serviceName = '' | |
189 | + this.isShowServiceFunctionName = false | |
190 | + if(this.commandType==0){ | |
191 | + this.callType = 'OneWay' | |
84 | 192 | } |
85 | - } | |
86 | - } | |
193 | + }, | |
194 | + | |
195 | + cancelTypeGap() { | |
196 | + this.isShowService = false | |
197 | + }, | |
198 | + | |
199 | + reset() { | |
200 | + this.callType = 'OneWay' | |
201 | + this.inputCommandVal = '' | |
202 | + }, | |
203 | + }, | |
204 | +} | |
87 | 205 | </script> |
88 | 206 | |
89 | 207 | <style lang="scss" scoped> |
90 | - .modal-content { | |
91 | - width: 720rpx; | |
92 | - padding: 0 30rpx; | |
93 | - background-color: white; | |
208 | +.modal-content { | |
209 | + width: 720rpx; | |
210 | + padding: 0 30rpx; | |
211 | + background-color: white; | |
94 | 212 | |
95 | - .header-title { | |
96 | - text-align: center; | |
97 | - font-weight: 700; | |
98 | - margin-bottom: 40rpx; | |
99 | - } | |
213 | + .header-title { | |
214 | + text-align: center; | |
215 | + font-weight: 700; | |
216 | + margin-bottom: 40rpx; | |
217 | + } | |
100 | 218 | |
101 | - .type-text { | |
102 | - color: #333; | |
103 | - font-size: 14px; | |
104 | - font-weight: 700; | |
105 | - margin-right: 30rpx; | |
106 | - } | |
219 | + .type-text { | |
220 | + color: #333; | |
221 | + font-size: 14px; | |
222 | + font-weight: 700; | |
223 | + margin-right: 30rpx; | |
224 | + } | |
107 | 225 | |
108 | - .content-body { | |
109 | - margin-top: 28rpx; | |
110 | - width: 100%; | |
111 | - } | |
226 | + .content-body { | |
227 | + margin-top: 28rpx; | |
228 | + width: 100%; | |
229 | + } | |
112 | 230 | |
113 | - .button-group { | |
114 | - display: flex; | |
115 | - margin-top: 40rpx; | |
116 | - justify-content: space-between; | |
231 | + .button-group { | |
232 | + display: flex; | |
233 | + margin-top: 40rpx; | |
234 | + justify-content: space-between; | |
117 | 235 | |
118 | - view { | |
119 | - width: 300rpx; | |
120 | - } | |
121 | - } | |
122 | - } | |
123 | -</style> | |
\ No newline at end of file | ||
236 | + view { | |
237 | + width: 300rpx; | |
238 | + } | |
239 | + } | |
240 | +} | |
241 | +</style> | ... | ... |
1 | +<template> | |
2 | + <view> | |
3 | + <!-- :label-style="{'width':'120rpx','overflow':'hidden','text-overflow':'ellipsis','white-space':'nowrap',display:'block'}" label-width="120rpx" --> | |
4 | + <u-form :model="seriesForm" ref="seriesRef"> | |
5 | + <u-form-item style="display: flex" v-for="(item, index) in seriesFunctionList" label-position="left" :key="item.identifier" :prop="item.identifier"> | |
6 | + <view v-if="!isTCPTransport"" :class="Array.isArray(item.dataType.specs)?'positionTop':'positionLeft'"> | |
7 | + <view class="text">{{ item.functionName }}</view> | |
8 | + <u-input | |
9 | + v-if="item.dataType.type == 'INT' || item.dataType.type == 'DOUBLE'" | |
10 | + shape="circle" | |
11 | + type="number" | |
12 | + v-model.number="seriesForm[item.identifier]" | |
13 | + :placeholder="`请输入${item.functionName}`" | |
14 | + @blur="(e)=>handleBlur(e,item.identifier)" | |
15 | + /> | |
16 | + <u-input v-if="item.dataType.type == 'TEXT'" shape="circle" type="text" v-model="seriesForm[item.identifier]" :placeholder="`请输入${item.functionName}`" /> | |
17 | + <view v-if="item.dataType.type == 'BOOL'" @click="handleBool(item.functionName, item.dataType, index)"> | |
18 | + <u-input | |
19 | + shape="circle" | |
20 | + v-model="seriesForm[item.identifier]" | |
21 | + :placeholder="`请选择${item.functionName}`" | |
22 | + disabled | |
23 | + disabledColor="#fff" | |
24 | + suffixIcon="arrow-down" | |
25 | + /> | |
26 | + <u-picker | |
27 | + :show="item.isShowModel" | |
28 | + :columns="[boolList]" | |
29 | + keyName="label" | |
30 | + @cancel="handleCancelBool(index)" | |
31 | + @close="handleCancelBool(index)" | |
32 | + @confirm="(e) => handleSelectBool(e, item.identifier, index)" | |
33 | + ></u-picker> | |
34 | + </view> | |
35 | + <view v-if="item.dataType.type == 'ENUM'" @click="handleEnum(item.functionName, item.dataType, index)"> | |
36 | + <u-input | |
37 | + shape="circle" | |
38 | + v-model="seriesForm[item.identifier]" | |
39 | + :placeholder="`请选择${item.functionName}`" | |
40 | + disabled | |
41 | + disabledColor="#fff" | |
42 | + suffixIcon="arrow-down" | |
43 | + /> | |
44 | + <u-picker | |
45 | + :show="item.isShowModel" | |
46 | + :columns="[enumList.map((item) => ({ label: item.name, value: item.value }))]" | |
47 | + keyName="label" | |
48 | + @cancel="handleCancelEnum(index)" | |
49 | + @close="handleCancelEnum(index)" | |
50 | + @confirm="(e) => handleSelectEnum(e, item.identifier, index)" | |
51 | + ></u-picker> | |
52 | + </view> | |
53 | + <template v-if="Array.isArray(item.dataType.specs)"> | |
54 | + <structuralForm class="seriesForm" :ref="item.identifier" :seriesInputData="item.dataType.specs || []"></structuralForm> | |
55 | + </template> | |
56 | + </view> | |
57 | + <view v-else class="positionLeft" style=" border: 1px dashed #f0f0f0; padding: 20rpx;"> | |
58 | + <view class="text">服务命令</view> | |
59 | + <u-input v-model="seriesForm.serviceCommand" type="text" shape="circle" :disabled="true"></u-input> | |
60 | + </view> | |
61 | + </u-form-item> | |
62 | + </u-form> | |
63 | + </view> | |
64 | +</template> | |
65 | + | |
66 | +<script> | |
67 | +import structuralForm from './structuralForm.vue' | |
68 | +export default { | |
69 | + name: 'StructForm', | |
70 | + props: { | |
71 | + seriesInputData: { | |
72 | + type: Array, | |
73 | + default: () => [], | |
74 | + }, | |
75 | + isTCPTransport:{ | |
76 | + type:Boolean, | |
77 | + default:false | |
78 | + } | |
79 | + }, | |
80 | + components: { | |
81 | + structuralForm, | |
82 | + }, | |
83 | + data() { | |
84 | + return { | |
85 | + boolInfo: {}, | |
86 | + enumInfo: {}, | |
87 | + seriesForm: {}, | |
88 | + seriesRules: {}, | |
89 | + seriesFunctionList: [], | |
90 | + boolList: [], | |
91 | + enumList: [], | |
92 | + } | |
93 | + }, | |
94 | + async mounted() { | |
95 | + await this.$nextTick(() => { | |
96 | + this.createInit() | |
97 | + }) | |
98 | + }, | |
99 | + watch: { | |
100 | + seriesInputData: { | |
101 | + deep: true, | |
102 | + handler(newVal, oldVal) { | |
103 | + this.createInit() | |
104 | + }, | |
105 | + }, | |
106 | + }, | |
107 | + | |
108 | + methods: { | |
109 | + createInit() { | |
110 | + this.seriesForm = {} | |
111 | + this.boolInfo = {} | |
112 | + this.seriesFunctionList = this.seriesInputData | |
113 | + for(const item of this.seriesInputData){ | |
114 | + this.$set(item, 'isShowModel', false)//动态添加picker的控制变量 | |
115 | + const { | |
116 | + dataType, | |
117 | + identifier, | |
118 | + functionName, | |
119 | + serviceCommand | |
120 | + } = item || {} | |
121 | + const {specs,type} = dataType || {} | |
122 | + if(this.isTCPTransport){ | |
123 | + this.$set(this.seriesForm,'serviceCommand',serviceCommand) | |
124 | + break; | |
125 | + } | |
126 | + if (Array.isArray(specs)) { | |
127 | + specs.forEach((itemSpecs) => { | |
128 | + this.$set(this.seriesForm, identifier, { | |
129 | + [itemSpecs.identifier]: '', | |
130 | + }) | |
131 | + }) | |
132 | + } else { | |
133 | + this.$set(this.seriesForm, identifier, '') | |
134 | + } | |
135 | + | |
136 | + //设置验证规则 | |
137 | + const { valueRange, length = 10240 } = specs || {} | |
138 | + const { max = 2147483647, min = -2147483647 } = valueRange || {} | |
139 | + if (type !== 'STRUCT') { | |
140 | + this.seriesRules[identifier] = [ | |
141 | + { required: true, message: type == 'BOOL' || type == 'ENUM' ? '请选择' : '请输入' + functionName }, | |
142 | + type == 'INT' || type == 'DOUBLE' | |
143 | + ? { | |
144 | + type: 'number', | |
145 | + trigger: 'change', | |
146 | + validator: (_rule, value) => { | |
147 | + const reg = /^[0-9]*$/ | |
148 | + if (!reg.test(value)) return Promise.reject(new Error(`${functionName}不是一个有效的数字`)) | |
149 | + if (value < min || value > max) return Promise.reject(new Error(`${functionName}取值范围在${min}~${max}之间`)) | |
150 | + | |
151 | + return Promise.resolve(value) | |
152 | + }, | |
153 | + } | |
154 | + : type == 'TEXT' | |
155 | + ? { | |
156 | + type: 'string', | |
157 | + trigger: 'change', | |
158 | + validator: (_rule, value) => { | |
159 | + if ((value?.length || 0) > length) return Promise.reject(new Error(`${functionName}数据长度应该小于${length}`)) | |
160 | + | |
161 | + return Promise.resolve(value) | |
162 | + }, | |
163 | + } | |
164 | + : {}, | |
165 | + ] | |
166 | + } | |
167 | + } | |
168 | + //设置验证规则 | |
169 | + this.$nextTick(() => { | |
170 | + !this.isTCPTransport && this.$refs.seriesRef.setRules(this.seriesRules) | |
171 | + }) | |
172 | + }, | |
173 | + isEmptyObject(obj) { | |
174 | + return Object.keys(obj).length === 0 && obj.constructor === Object | |
175 | + }, | |
176 | + | |
177 | + //打开Bool的picker | |
178 | + handleBool(name, dataType, num) { | |
179 | + this.seriesFunctionList.forEach((item, index) => { | |
180 | + if (index == num) { | |
181 | + item.isShowModel = true | |
182 | + } | |
183 | + }) | |
184 | + const { specs } = dataType || {} | |
185 | + const { boolClose, boolOpen } = specs || {} | |
186 | + console.log(boolClose, boolOpen,'boolClose, boolOpen') | |
187 | + if(!boolClose&&!boolOpen){ | |
188 | + uni.$u.toast(`暂无可选的${name}`) | |
189 | + this.boolList = [] | |
190 | + return | |
191 | + } | |
192 | + this.boolList = [ | |
193 | + { label: boolClose + '-0', value: 0 }, | |
194 | + { label: boolOpen + '-1', value: 1 }, | |
195 | + ] | |
196 | + }, | |
197 | + handleSelectBool(e, name, num) { | |
198 | + const { value } = e || {} | |
199 | + this.boolInfo[name] = value[0].value | |
200 | + this.$set(this.seriesForm, name, value[0].label) | |
201 | + this.seriesFunctionList.forEach((item, index) => { | |
202 | + if (index == num) { | |
203 | + item.isShowModel = false | |
204 | + } | |
205 | + }) | |
206 | + }, | |
207 | + handleBlur(value,name){ | |
208 | + if(!value) return | |
209 | + this.$set(this.seriesForm,name,Number(value)) | |
210 | + }, | |
211 | + | |
212 | + //打开Enum的picker | |
213 | + handleEnum(name, dataType, num) { | |
214 | + const { specsList } = dataType || {} | |
215 | + this.enumList = specsList || [] | |
216 | + console.log(this.enumList,'enumInfo') | |
217 | + if(!this.enumList.length){ | |
218 | + return uni.$u.toast(`暂无可选的${name}`) | |
219 | + } | |
220 | + this.seriesFunctionList.forEach((item, index) => { | |
221 | + if (index == num) { | |
222 | + item.isShowModel = true | |
223 | + } | |
224 | + }) | |
225 | + }, | |
226 | + //确定选中 | |
227 | + handleSelectEnum(e, name, num) { | |
228 | + const { value } = e || {} | |
229 | + this.enumInfo[name] = value[0].value | |
230 | + this.$set(this.seriesForm, name, value[0].label) | |
231 | + this.seriesFunctionList.forEach((item, index) => { | |
232 | + if (index == num) { | |
233 | + item.isShowModel = false | |
234 | + } | |
235 | + }) | |
236 | + }, | |
237 | + | |
238 | + | |
239 | + //关闭Enum和Bool的picker弹框 | |
240 | + handleCancelEnum(num){ | |
241 | + this.seriesFunctionList.forEach((item, index) => { | |
242 | + if (index == num) { | |
243 | + item.isShowModel = false | |
244 | + } | |
245 | + }) | |
246 | + }, | |
247 | + handleCancelBool(num){ | |
248 | + this.seriesFunctionList.forEach((item, index) => { | |
249 | + if (index == num) { | |
250 | + item.isShowModel = false | |
251 | + } | |
252 | + }) | |
253 | + }, | |
254 | + | |
255 | + // 获取表单数据 | |
256 | + getFormField() { | |
257 | + const keys = Object.keys(this.seriesForm) | |
258 | + for (let i = 0; i < keys.length; i++) { | |
259 | + const key = keys[i] | |
260 | + if (Array.isArray(this.$refs[key])) { | |
261 | + const values = this.$refs[key][0]?.getFormField() | |
262 | + if (!this.isEmptyObject(values)) { | |
263 | + this.seriesForm[key] = values | |
264 | + } | |
265 | + } | |
266 | + } | |
267 | + return { | |
268 | + ...this.seriesForm, | |
269 | + ...this.boolInfo, | |
270 | + ...this.enumInfo, | |
271 | + } | |
272 | + }, | |
273 | + | |
274 | + | |
275 | + handleValidate() { | |
276 | + const keys = Object.keys(this.seriesForm) | |
277 | + for (let i = 0; i < keys.length; i++) { | |
278 | + const key = keys[i] | |
279 | + if (Array.isArray(this.$refs[key])) { | |
280 | + this.$refs[key][0]?.handleValidate(valid=>{ | |
281 | + if(!valid) return false | |
282 | + }) | |
283 | + } else { | |
284 | + this.$refs.seriesRef.validate(valid=>{ | |
285 | + if(!valid) return false | |
286 | + }) | |
287 | + } | |
288 | + return true | |
289 | + } | |
290 | + }, | |
291 | + }, | |
292 | +} | |
293 | +</script> | |
294 | + | |
295 | +<style lang="scss" scoped> | |
296 | +.positionLeft { | |
297 | + display: flex; | |
298 | + align-items: center; | |
299 | + .text{ | |
300 | + | |
301 | + max-width: 190rpx; | |
302 | + overflow: hidden; | |
303 | + text-overflow: ellipsis; | |
304 | + white-space: nowrap; | |
305 | + } | |
306 | +} | |
307 | + | |
308 | +.positionTop { | |
309 | + display: flex; | |
310 | + flex-direction: column; | |
311 | + border: 1px dashed #f0f0f0; | |
312 | + padding: 20rpx; | |
313 | + .seriesForm{ | |
314 | + margin-left:30rpx | |
315 | + } | |
316 | +} | |
317 | +</style> | ... | ... |
1 | +<template> | |
2 | + <view> | |
3 | + <u-form :model="seriesForm" ref="seriesRef" :label-style="{ width: '160rpx', overflow: 'hidden', 'text-overflow': 'ellipsis', 'white-space': 'nowrap', display: 'block' }" label-width="160rpx"> | |
4 | + <u-form-item v-for="(item, index) in seriesFunctionList" :key="item.identifier" :label="item.functionName" :prop="item.identifier"> | |
5 | + <u-input v-if="item.dataType.type == 'INT' || item.dataType.type == 'DOUBLE'" shape="circle" type="number" v-model="seriesForm[item.identifier]" :placeholder="`请输入${item.functionName}`" /> | |
6 | + <u-input v-if="item.dataType.type == 'TEXT'" shape="circle" type="text" v-model="seriesForm[item.identifier]" :placeholder="`请输入${item.functionName}`" /> | |
7 | + <view @click="handleBool(item.identifier, item.dataType, index)"> | |
8 | + <u-input | |
9 | + v-if="item.dataType.type == 'BOOL'" | |
10 | + shape="circle" | |
11 | + v-model="seriesForm[item.identifier]" | |
12 | + :placeholder="`请选择${item.functionName}`" | |
13 | + disabled | |
14 | + disabledColor="#fff" | |
15 | + suffixIcon="arrow-down" | |
16 | + /> | |
17 | + <u-picker | |
18 | + :show="item.isShowModel" | |
19 | + :columns="[boolList]" | |
20 | + keyName="label" | |
21 | + closeOnClickOverlay | |
22 | + @cancel="handleCancel(index)" | |
23 | + @close="handleCancel(index)" | |
24 | + @confirm="(e) => handleSelectBool(e, item.identifier, index)" | |
25 | + ></u-picker> | |
26 | + </view> | |
27 | + | |
28 | + <view @click="handleEnum(item.identifier, item.dataType, index)"> | |
29 | + <u-input | |
30 | + v-if="item.dataType.type == 'ENUM'" | |
31 | + shape="circle" | |
32 | + v-model="seriesForm[item.identifier]" | |
33 | + :placeholder="`请选择${item.functionName}`" | |
34 | + disabled | |
35 | + disabledColor="#fff" | |
36 | + suffixIcon="arrow-down" | |
37 | + /> | |
38 | + <u-picker | |
39 | + :show="item.isShowModel" | |
40 | + :columns="[enumList.map((item) => ({ label: item.name, value: item.value }))]" | |
41 | + keyName="label" | |
42 | + closeOnClickOverlay | |
43 | + @cancel="handleCancel(index)" | |
44 | + @close="handleCancel(index)" | |
45 | + @confirm="(e) => handleSelectEnum(e, item.identifier, index)" | |
46 | + ></u-picker> | |
47 | + </view> | |
48 | + </u-form-item> | |
49 | + </u-form> | |
50 | + </view> | |
51 | +</template> | |
52 | + | |
53 | +<script> | |
54 | +export default { | |
55 | + name: 'StructForm', | |
56 | + props: { | |
57 | + seriesInputData: { | |
58 | + type: Array, | |
59 | + default: () => [], | |
60 | + }, | |
61 | + }, | |
62 | + data() { | |
63 | + return { | |
64 | + boolInfo: {}, | |
65 | + enumInfo: {}, | |
66 | + seriesForm: {}, | |
67 | + seriesRules: {}, | |
68 | + seriesFunctionList: [], | |
69 | + boolList: [], | |
70 | + enumList: [], | |
71 | + } | |
72 | + }, | |
73 | + async mounted() { | |
74 | + await this.$nextTick(() => { | |
75 | + this.createInit() | |
76 | + }) | |
77 | + }, | |
78 | + watch: { | |
79 | + seriesInputData: { | |
80 | + deep: true, | |
81 | + handler(newVal, oldVal) { | |
82 | + this.createInit() | |
83 | + }, | |
84 | + }, | |
85 | + }, | |
86 | + | |
87 | + methods: { | |
88 | + createInit() { | |
89 | + this.seriesFunctionList = this.seriesInputData | |
90 | + this.seriesInputData.forEach((item) => { | |
91 | + this.$set(item, 'isShowModel', false) | |
92 | + const { | |
93 | + dataType: { specs, type }, | |
94 | + identifier, | |
95 | + functionName, | |
96 | + } = item || {} | |
97 | + | |
98 | + this.$set(this.seriesForm, identifier, '') | |
99 | + const { valueRange, length = 10240 } = specs || {} | |
100 | + | |
101 | + const { max = 2147483647, min = -2147483647 } = valueRange || {} | |
102 | + | |
103 | + this.seriesRules[identifier] = [ | |
104 | + { required: true, message: type == 'BOOL' || type == 'ENUM' ? '请选择' : '请输入' + functionName }, | |
105 | + type == 'INT' || type == 'DOUBLE' | |
106 | + ? { | |
107 | + type: 'number', | |
108 | + trigger: 'change', | |
109 | + validator: (_rule, value) => { | |
110 | + const reg = /^[0-9]*$/ | |
111 | + if (!reg.test(value)) return Promise.reject(new Error(`${functionName}不是一个有效的数字`)) | |
112 | + if (value < min || value > max) return Promise.reject(new Error(`${functionName}取值范围在${min}~${max}之间`)) | |
113 | + | |
114 | + return Promise.resolve(value) | |
115 | + }, | |
116 | + } | |
117 | + : type == 'TEXT' | |
118 | + ? { | |
119 | + type: 'string', | |
120 | + trigger: 'change', | |
121 | + validator: (_rule, value) => { | |
122 | + if ((value?.length || 0) > length) return Promise.reject(new Error(`${functionName}数据长度应该小于${length}`)) | |
123 | + | |
124 | + return Promise.resolve(value) | |
125 | + }, | |
126 | + } | |
127 | + : {}, | |
128 | + ] | |
129 | + }) | |
130 | + this.$nextTick(() => { | |
131 | + this.$refs.seriesRef.setRules(this.seriesRules) | |
132 | + }) | |
133 | + }, | |
134 | + | |
135 | + handleBool(name, dataType, num) { | |
136 | + this.seriesFunctionList.forEach((item, index) => { | |
137 | + if (index == num) { | |
138 | + item.isShowModel = true | |
139 | + } | |
140 | + }) | |
141 | + const { specs } = dataType || {} | |
142 | + const { boolClose, boolOpen } = specs || {} | |
143 | + this.boolList = [ | |
144 | + { label: boolClose + '0', value: 0 }, | |
145 | + { label: boolOpen + '0', value: 1 }, | |
146 | + ] | |
147 | + this.isShowBool = true | |
148 | + }, | |
149 | + handleSelectBool(e, name, num) { | |
150 | + this.isShowBool = false | |
151 | + const { value } = e || {} | |
152 | + this.boolInfo[name] = value[0].value | |
153 | + this.$set(this.seriesForm, name, value[0].label) | |
154 | + this.seriesFunctionList.forEach((item, index) => { | |
155 | + if (index == num) { | |
156 | + item.isShowModel = false | |
157 | + } | |
158 | + }) | |
159 | + }, | |
160 | + | |
161 | + getFormField() { | |
162 | + return { | |
163 | + ...this.seriesForm, | |
164 | + ...this.boolInfo, | |
165 | + ...this.enumInfo, | |
166 | + } | |
167 | + }, | |
168 | + | |
169 | + handleEnum(name, dataType, num) { | |
170 | + const { specsList } = dataType || {} | |
171 | + this.isShowEnum = true | |
172 | + this.enumList = specsList || [] | |
173 | + if(!this.enumList.length){ | |
174 | + return uni.$u.toast(`暂无可选的${name}`) | |
175 | + } | |
176 | + this.seriesFunctionList.forEach((item, index) => { | |
177 | + if (index == num) { | |
178 | + item.isShowModel = true | |
179 | + } | |
180 | + }) | |
181 | + }, | |
182 | + handleSelectEnum(e, name, num) { | |
183 | + const { value } = e || {} | |
184 | + this.enumInfo[name] = value[0].value | |
185 | + this.$set(this.seriesForm, name, value[0].label) | |
186 | + this.seriesFunctionList.forEach((item, index) => { | |
187 | + if (index == num) { | |
188 | + item.isShowModel = false | |
189 | + } | |
190 | + }) | |
191 | + }, | |
192 | + handleCancel(num){ | |
193 | + this.seriesFunctionList.forEach((item, index) => { | |
194 | + if (index == num) { | |
195 | + item.isShowModel = false | |
196 | + } | |
197 | + }) | |
198 | + }, | |
199 | + | |
200 | + handleValidate() { | |
201 | + return this.$refs.seriesRef?.validate((valid) => { | |
202 | + if (!valid) return | |
203 | + }) | |
204 | + }, | |
205 | + }, | |
206 | +} | |
207 | +</script> | |
208 | + | |
209 | +<style lang="scss" scoped></style> | ... | ... |