Commit 1c2f21524b460b5e618280b89e13e7e507efc89a
Committed by
xp.Huang
1 parent
fe27c91c
feat: 设备命令下发添加服务调用
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> | ... | ... |