detail.vue 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. <template>
  2. <view class="content">
  3. <view class="header" style="margin:0;padding: 0px;">
  4. <view class="bg" style="position: fixed;top:0px;bottom:0px;left:0px;right:0px;background-image: url('https://hyscancode.oss-cn-hangzhou.aliyuncs.com/jianyaoji/images/device/bg.png');background-size: 100% 100%;padding:0 40rpx">
  5. <view style="position: absolute;right:10rpx;top:30rpx;z-index: 9999">
  6. <u-button @click="reportError" color="#EC6D2F" text="上报异常" type="error" size="mini"></u-button>
  7. </view>
  8. <view style="position: absolute;right:52rpx;top:116rpx;" v-if="deviceInfo.status == 3">
  9. <view>
  10. <u-image :showLoading="true" src="/static/images/device/zaixian.png" width="200rpx" height="200rpx" ></u-image>
  11. </view>
  12. <view style="position: absolute;color:#48C373;top:110rpx;left:70rpx">在线</view>
  13. </view>
  14. <view style="position: absolute;right:52rpx;top:116rpx;" v-if="deviceInfo.status == 4">
  15. <view>
  16. <u-image :showLoading="true" src="/static/images/device/offline.png" width="200rpx" height="200rpx" ></u-image>
  17. </view>
  18. <view style="position: absolute;color:lightgray;top:110rpx;left:70rpx">离线</view>
  19. </view>
  20. <view style="font-weight: bold;font-size: 30rpx;margin-top:20rpx">
  21. {{deviceInfo.deviceName}}
  22. </view>
  23. <view class="prop-text">
  24. <text class="text-left">所属用户:</text>
  25. <text class="text-right">{{sysDept.deptName}}</text>
  26. </view>
  27. <view class="prop-text">
  28. <text class="text-left">设备编号:</text>
  29. <text class="text-right">{{deviceInfo.serialNumber}}</text>
  30. </view>
  31. <view class="prop-text">
  32. <text class="text-left">二维码ID:</text>
  33. <text class="text-right">{{deviceInfo.qrcodeId==null?"未绑定":deviceInfo.qrcodeId}}</text>
  34. </view>
  35. <view class="prop-text">
  36. <text class="text-left">激活时间:</text>
  37. <text class="text-right">{{deviceInfo.activeTime}}</text>
  38. </view>
  39. <view class="prop-text">
  40. <text class="text-left">查看位置:</text>
  41. <view style="position: absolute;top:0px;left:120rpx">
  42. <u-icon name="map-fill" label="查看位置" color="#2979ff" size="20" labelSize="12"></u-icon>
  43. </view>
  44. </view>
  45. <view class="prop-card-area">
  46. <view class="prop-card">
  47. <view class="prop-card-top">{{ deviceInfo.rssi }}</view>
  48. <view class="prop-card-bottom">设备信号</view>
  49. </view>
  50. <view class="prop-card">
  51. <view class="prop-card-top">{{ timeobj.alltime }}</view>
  52. <view class="prop-card-bottom">累计时长</view>
  53. </view>
  54. <view class="prop-card">
  55. <view class="prop-card-top">{{ timeobj.avgtime }}</view>
  56. <view class="prop-card-bottom">平均时长</view>
  57. </view>
  58. </view>
  59. <view class="tab-area">
  60. <u-tabs lineWidth="80" :activeStyle="{ color: '#3E9CFC' }" :list="summary" keyName="tabName" @change="getDeviceStatus"></u-tabs>
  61. <view>
  62. <uni-row class="demo-uni-row">
  63. <uni-col :span="12">
  64. </uni-col>
  65. </uni-row>
  66. <uni-row class="demo-uni-row">
  67. <uni-col :span="12">
  68. </uni-col>
  69. </uni-row>
  70. <view v-if="deviceInfo.status == 3">
  71. <view class="prop-item">
  72. <view class="prop-item-left">开机</view>
  73. <view class="prop-item-right"> <u-switch inactiveColor="lightgray" activeColor="rgb(96,195,113)" v-model="power.value" :loading ="power.loading" @change="changeProp('PowerControl')"></u-switch>
  74. </view>
  75. </view>
  76. <view class="prop-item">
  77. <view class="prop-item-left">锁定</view>
  78. <view class="prop-item-right">
  79. <u-switch inactiveColor="lightgray" activeColor="rgb(96,195,113)" v-model="lock.value" :loading ="lock.loading" @change="changeProp('LockControl')"></u-switch>
  80. </view>
  81. </view>
  82. </view>
  83. <view v-if="deviceInfo.status != 3">
  84. <view class="prop-item">
  85. <view class="prop-item-left">开机</view>
  86. <view class="prop-item-right"> <u-switch inactiveColor="lightgray" activeColor="rgb(96,195,113)" value="0" :loading ="power.loading" @change="changeProp('PowerControl')"></u-switch>
  87. </view>
  88. </view>
  89. <view class="prop-item">
  90. <view class="prop-item-left">锁定</view>
  91. <view class="prop-item-right">
  92. <u-switch inactiveColor="lightgray" activeColor="rgb(96,195,113)" value="0" :loading ="lock.loading" @change="changeProp('LockControl')"></u-switch>
  93. </view>
  94. </view>
  95. </view>
  96. <view v-for="item in inputProp" v-if="checkCommonProp(item)">
  97. <view class="prop-item">
  98. <view class="prop-item-left">{{item.name}}</view>
  99. <view class="prop-item-right">
  100. <view v-if="item.type === 'integer'" style="width:240rpx;position: absolute;left: -260rpx;top: 10rpx;;">
  101. <u-input :customStyle="inputStyle" fontSize="20rpx" placeholder="请输入整数" v-model="item.shadow">
  102. </u-input>
  103. </view>
  104. <view style="padding:0 10rpx;width:260rpx;position: absolute;left: -270rpx;top: -10rpx;" v-if="item.type === 'string'">
  105. <u-input :customStyle="inputStyle" fontSize="20rpx" placeholder="请输入字符串" v-model="item.shadow">
  106. </u-input>
  107. </view>
  108. <view style="padding:0 10rpx;width:260rpx;position: absolute;left: -270rpx;top: -10rpx;" v-if="item.type === 'array'">
  109. <u-input :customStyle="inputStyle" fontSize="20rpx" placeholder="请使用英文逗号分隔的字符串" v-model="item.shadow">
  110. </u-input>
  111. </view>
  112. <view style="padding:0 10rpx;position: absolute;left: -300rpx;top: -10rpx;" v-if="item.type === 'enum'">
  113. <u-cell customStyle="border:0px;" size="mini" @click="chooseItemData(item)" arrow-direction="left" v-if="item.text != null && item.text.length>0" :title="item.text">
  114. <u-icon slot="icon" size="12" name="arrow-down"></u-icon>
  115. </u-cell>
  116. <u-cell customStyle="border:0px;" size="mini" @click="chooseItemData(item)" arrow-direction="down" v-else title="请选择">
  117. <u-icon slot="icon" size="12" name="arrow-down"></u-icon>
  118. </u-cell>
  119. </view>
  120. <view class="item" style="padding-top:10rpx">
  121. <u-button v-if="deviceInfo.status==3"
  122. @tap="send(item)"
  123. text="发送"
  124. type="success"
  125. size="mini"
  126. ></u-button>
  127. <u-button v-if="deviceInfo.status!=3"
  128. @tap="send(item)"
  129. text="发送"
  130. type="info"
  131. size="mini"
  132. ></u-button>
  133. </view>
  134. </view>
  135. </view>
  136. </view>
  137. <view style="margin-top:20rpx;margin-left:20rpx">
  138. <u--text text="属性监控" style="margin-top:20px"></u--text>
  139. </view>
  140. <view class="prop-item" v-if="checkCommonProp(item)" v-for="item in deviceInfo.readOnlyList">
  141. <view class="prop-item-left">{{item.name}}</view>
  142. <view class="prop-item-right" style="top:0rpx;">
  143. <u-input v-if="checkItemValue(item)" placeholder="请输入字符串" disabled="" :value="item.shadow+' '+item.unit">
  144. </u-input>
  145. </view>
  146. </view>
  147. </view>
  148. </view>
  149. </view>
  150. <!-- <u&#45;&#45;form>-->
  151. <!-- <u-form-item style="border:0px"-->
  152. <!-- label="设备名称:"-->
  153. <!-- labelWidth="auto"-->
  154. <!-- :borderBottom="false"-->
  155. <!-- >-->
  156. <!-- <u&#45;&#45;input disabled-->
  157. <!-- placeholder="请选择分组"-->
  158. <!-- border="none"-->
  159. <!-- color="gray"-->
  160. <!-- disabledColor="white"-->
  161. <!-- v-model="deviceInfo.deviceName"-->
  162. <!-- ></u&#45;&#45;input>-->
  163. <!-- </u-form-item>-->
  164. <!-- <u-form-item style="border:0px"-->
  165. <!-- label="所属机构:"-->
  166. <!-- labelWidth="auto"-->
  167. <!-- :borderBottom="false"-->
  168. <!-- >-->
  169. <!-- <u&#45;&#45;input v-if="sysDept != null" disabled-->
  170. <!-- placeholder="请选择分组22"-->
  171. <!-- border="none"-->
  172. <!-- color="gray"-->
  173. <!-- disabledColor="white"-->
  174. <!-- :value="sysDept.deptName"-->
  175. <!-- ></u&#45;&#45;input>-->
  176. <!-- <u&#45;&#45;input v-else-if="deviceInfo.deptId == -1" disabled-->
  177. <!-- placeholder="请选择分组11"-->
  178. <!-- border="none"-->
  179. <!-- color="gray"-->
  180. <!-- disabledColor="white"-->
  181. <!-- value="已注销"-->
  182. <!-- ></u&#45;&#45;input>-->
  183. <!-- <u&#45;&#45;input v-else disabled-->
  184. <!-- placeholder="未绑定"-->
  185. <!-- border="none"-->
  186. <!-- color="gray"-->
  187. <!-- disabledColor="white"-->
  188. <!-- value="未绑定"-->
  189. <!-- ></u&#45;&#45;input>-->
  190. <!-- </u-form-item>-->
  191. <!-- <u-form-item style="border:0px"-->
  192. <!-- label="设备编号:"-->
  193. <!-- labelWidth="auto"-->
  194. <!-- :borderBottom="false"-->
  195. <!-- ref="item1"-->
  196. <!-- >-->
  197. <!-- <u&#45;&#45;input disabled-->
  198. <!-- border="none"-->
  199. <!-- color="gray"-->
  200. <!-- disabledColor="white"-->
  201. <!-- v-model="deviceInfo.serialNumber"-->
  202. <!-- ></u&#45;&#45;input>-->
  203. <!-- </u-form-item>-->
  204. <!-- <u-form-item style="border:0px"-->
  205. <!-- label="二维码ID:"-->
  206. <!-- labelWidth="auto"-->
  207. <!-- :borderBottom="false"-->
  208. <!-- ref="item1"-->
  209. <!-- >-->
  210. <!-- <u&#45;&#45;input disabled-->
  211. <!-- border="none"-->
  212. <!-- color="gray"-->
  213. <!-- disabledColor="white"-->
  214. <!-- v-model="deviceInfo.qrcodeId"-->
  215. <!-- ></u&#45;&#45;input>-->
  216. <!-- </u-form-item>-->
  217. <!-- <u-form-item style="border:0px"-->
  218. <!-- label="激活时间:"-->
  219. <!-- labelWidth="auto"-->
  220. <!-- :borderBottom="false"-->
  221. <!-- ref="item1"-->
  222. <!-- >-->
  223. <!-- <u&#45;&#45;input disabled-->
  224. <!-- border="none"-->
  225. <!-- color="gray"-->
  226. <!-- disabledColor="white"-->
  227. <!-- :value="deviceInfo.activeTime"-->
  228. <!-- ></u&#45;&#45;input>-->
  229. <!-- </u-form-item>-->
  230. <!-- <u-form-item style="border:0px"-->
  231. <!-- label="运行时长:"-->
  232. <!-- labelWidth="auto"-->
  233. <!-- :borderBottom="false"-->
  234. <!-- ref="item1"-->
  235. <!-- >-->
  236. <!-- <view style="width: 20%">-->
  237. <!-- <u-button @click="seeTime()" type="primary" size="mini" text="查看时长"></u-button>-->
  238. <!-- </view>-->
  239. <!-- </u-form-item>-->
  240. <!-- <u-form-item style="border:0px"-->
  241. <!-- label="位置信息:"-->
  242. <!-- labelWidth="auto"-->
  243. <!-- :borderBottom="false"-->
  244. <!-- ref="item1"-->
  245. <!-- >-->
  246. <!-- <view style="width: 20%">-->
  247. <!-- <u-button @click="openLocation(deviceInfo)" type="primary" size="mini" text="查看位置"></u-button>-->
  248. <!-- </view>-->
  249. <!-- </u-form-item>-->
  250. <!-- <u-form-item style="border:0px"-->
  251. <!-- label="设备信号:"-->
  252. <!-- labelWidth="auto"-->
  253. <!-- :borderBottom="false"-->
  254. <!-- >-->
  255. <!-- <u&#45;&#45;input disabled-->
  256. <!-- border="none"-->
  257. <!-- color="gray"-->
  258. <!-- disabledColor="white"-->
  259. <!-- :value="deviceInfo.rssi"-->
  260. <!-- ></u&#45;&#45;input>-->
  261. <!-- </u-form-item>-->
  262. <!-- <u-form-item v-if="deviceInfo.networkAddress != null" style="border:0px"-->
  263. <!-- label="设备网络:"-->
  264. <!-- labelWidth="auto"-->
  265. <!-- :borderBottom="false"-->
  266. <!-- ref="item1"-->
  267. <!-- >-->
  268. <!-- <u&#45;&#45;input disabled-->
  269. <!-- border="none"-->
  270. <!-- color="gray"-->
  271. <!-- disabledColor="white"-->
  272. <!-- :value="deviceInfo.networkAddress+'('+deviceInfo.networkIp+')'"-->
  273. <!-- ></u&#45;&#45;input>-->
  274. <!-- </u-form-item>-->
  275. <!-- <u-form-item style="border:0px"-->
  276. <!-- label="设备状态:"-->
  277. <!-- labelWidth="auto"-->
  278. <!-- :borderBottom="false"-->
  279. <!-- >-->
  280. <!-- <view style="width:90rpx">-->
  281. <!-- <u-tag v-if="deviceInfo.status === 4" text="离线" size="mini" type="info"></u-tag>-->
  282. <!-- <u-tag v-if="deviceInfo.status === 3" text="在线" size="mini" type="success"></u-tag>-->
  283. <!-- <u-tag v-if="deviceInfo.status === 2" text="禁用" size="mini" type="error"></u-tag>-->
  284. <!-- <u-tag v-if="deviceInfo.status === 1" text="未激活" size="mini" type="error"></u-tag>-->
  285. <!-- </view>-->
  286. <!-- </u-form-item>-->
  287. <!-- </u&#45;&#45;form>-->
  288. <!-- </view>-->
  289. <!-- <view class="text-area">-->
  290. <!-- <u-tabs :list="summary" keyName="tabName" @change="getDeviceStatus"></u-tabs>-->
  291. <!-- <view class="prop-container" style="background: white;padding:10px ">-->
  292. <!-- <u-form>-->
  293. <!-- <u-form-item style="border:0px;position: relative"-->
  294. <!-- label=" "-->
  295. <!-- labelWidth="auto"-->
  296. <!-- :borderBottom="false"-->
  297. <!-- >-->
  298. <!-- <uni-row class="demo-uni-row">-->
  299. <!-- <uni-col :span="12">-->
  300. <!-- 开机: <u-switch v-model="power.value" :loading ="power.loading" @change="changeProp('PowerControl')"></u-switch>-->
  301. <!-- </uni-col>-->
  302. <!-- <uni-col :span="12">-->
  303. <!-- 锁定: <u-switch v-model="lock.value" :loading ="lock.loading" @change="changeProp('LockControl')"></u-switch>-->
  304. <!-- </uni-col>-->
  305. <!-- </uni-row>-->
  306. <!-- </u-form-item>-->
  307. <!-- <u-line></u-line>-->
  308. <!-- <view>-->
  309. <!-- <uni-row class="demo-uni-row" v-if="checkCommonProp(item)" v-for="item in deviceInfo.readOnlyList">-->
  310. <!-- <uni-col :span="10">-->
  311. <!-- <view class="item">{{item.name}}</view>-->
  312. <!-- </uni-col>-->
  313. <!-- <uni-col :span="10">-->
  314. <!-- <view style="padding:0 10rpx">-->
  315. <!-- <u-input placeholder="请输入字符串" disabled="" :value="item.shadow+' '+item.unit">-->
  316. <!-- </u-input>-->
  317. <!-- </view>-->
  318. <!-- </uni-col>-->
  319. <!-- </uni-row>-->
  320. <!-- </view>-->
  321. <!-- </u-form>-->
  322. <!-- </view>-->
  323. <u-picker @cancel="show=null" :show="show!=null" :columns="columns" @confirm="confirmItemData" keyName="text"></u-picker>
  324. <u-modal @cancel="cancel" :show="showErrDlg" title="异常上报" :showCancelButton="true" @confirm="doReportError" >
  325. <view class="slot-content">
  326. <view style="margin:10px">
  327. <textarea auto-height ="true" v-model="errorMsg" placeholder="请输入异常内容" ></textarea>
  328. </view>
  329. </view>
  330. </u-modal>
  331. <!-- <u-modal :show="showTimeDlg" title="查看时长" @confirm="closeTime" >-->
  332. <!-- <view class="slot-content">-->
  333. <!-- <view v-if="timeobj!=null && deviceInfo.status == 3">本次运行时长:{{ timeobj.runtime }}</view>-->
  334. <!-- <view v-if="timeobj!=null ">累计时长:{{ timeobj.alltime }}</view>-->
  335. <!-- <view v-if="timeobj!=null ">平均每天运行时长:{{ timeobj.avgtime }}</view>-->
  336. <!-- </view>-->
  337. <!-- </u-modal>-->
  338. </view>
  339. </view>
  340. </template>
  341. <script>
  342. import { getDetail,getDeviceStatus,cacheJsonThingsModel,reportError ,getDeviceRunTime} from '@/api/device/device.js'
  343. import UButton from "../../uni_modules/uview-ui/components/u-button/u-button";
  344. import UForm from "../../uni_modules/uview-ui/components/u--form/u--form";
  345. import UInput from "../../uni_modules/uview-ui/components/u--input/u--input";
  346. import UImage from "../../uni_modules/uview-ui/components/u--image/u--image";
  347. export default {
  348. components: {UImage, UInput, UForm, UButton},
  349. data(){
  350. return {
  351. inputStyle:{height:'44rpx','fontSize':'20rpx'},
  352. modelKey:['PowerControl','LockControl'],
  353. power:{
  354. loading:true,
  355. value:0,
  356. },
  357. lock:{
  358. loading:true,
  359. value:0,
  360. },
  361. showTimeDlg:false,
  362. showErrDlg:null,
  363. errorMsg:"asdasdasdasd",
  364. show:null,
  365. value:"",
  366. deviceInfo:{},
  367. id:0,
  368. summary:[],
  369. activeName:0,
  370. childId:"",
  371. oneToMul:false,
  372. inputProp:[],
  373. watchProp:[],
  374. columns:[],
  375. location:{},
  376. sysDept:null,
  377. timeobj:{alltime:0,avgtime:0},
  378. publishMsg:false,
  379. checkTimer:null,
  380. }
  381. },
  382. onLoad: function(opt) {
  383. this.id = opt.id;
  384. // this.id = 61;
  385. this.connectMqtt();
  386. this.getDetail();
  387. },
  388. destroyed() {
  389. // 取消订阅主题
  390. this.mqttUnSubscribe(this.deviceInfo);
  391. clearInterval(this.checkTimer);
  392. },
  393. methods:{
  394. checkItemValue(item){
  395. if(!item.shadow){
  396. item.shadow = 0;
  397. }
  398. if(!item.unit){
  399. item.unit = "";
  400. }
  401. return true;
  402. },
  403. changeProp(key){
  404. let item = null;
  405. let obj = null;
  406. if(key === "LockControl"){
  407. item = this.lock;
  408. }else if(key === "PowerControl"){
  409. item = this.power;
  410. }
  411. let value = item.value;
  412. obj = item.item;
  413. if(value){
  414. obj.value = 1;
  415. obj.shadow = 1;
  416. }else{
  417. obj.value = 0;
  418. obj.shadow = 0;
  419. }
  420. this.publishThingsModel(this.deviceInfo,obj);
  421. },
  422. checkCommonProp(item){
  423. let id =item.id;
  424. let isCommonKey = this.modelKey.some(res=>{
  425. return id === res;
  426. })
  427. return !isCommonKey;
  428. },
  429. getRunTime(){
  430. let self = this;
  431. getDeviceRunTime(this.id).then(res=>{
  432. self.timeobj = res.data;
  433. if(self.timeobj.avgtime == ""){
  434. self.timeobj.avgtime = 0;
  435. }
  436. if(self.timeobj.alltime == ""){
  437. self.timeobj.alltime = 0;
  438. }
  439. })
  440. },
  441. seeTime(){
  442. this.showTimeDlg = true;
  443. this.getRunTime();
  444. },
  445. closeTime(){
  446. this.showTimeDlg = false;
  447. },
  448. cancel(){
  449. this.showErrDlg = false;
  450. },
  451. reportError(){
  452. this.showErrDlg = true;
  453. },
  454. doReportError(){
  455. if(!this.errorMsg){
  456. this.$modal.showToast('异常信息不能为空')
  457. }else{
  458. let self = this;
  459. let errObj = {};
  460. errObj.deviceId = this.deviceInfo.deviceId;
  461. errObj.deviceName = this.deviceInfo.deviceName
  462. errObj.desc = this.errorMsg;
  463. errObj.deptId= this.deviceInfo.deptId;
  464. reportError(errObj).then(res=>{
  465. self.$modal.showToast(res.msg)
  466. self.cancel();
  467. })
  468. }
  469. },
  470. openLocation(){
  471. uni.openLocation({
  472. latitude: this.location.latitude,
  473. longitude: this.location.longitude,
  474. success: function () {
  475. console.log('success');
  476. }
  477. });
  478. },
  479. confirmItemData(e){
  480. let data = e.value[0];
  481. this.show.text = data.text;
  482. this.show.shadow=data.value;
  483. this.show = null;
  484. console.log(data);
  485. },
  486. send(item){
  487. if(this.deviceInfo.status != 3){
  488. uni.showToast({
  489. title:"设备暂未上线",
  490. icon:"error"
  491. })
  492. return;
  493. }
  494. if(!item.shadow){
  495. uni.showToast({
  496. title:"数据不能为空",
  497. icon:"error"
  498. })
  499. return;
  500. }
  501. this.publishThingsModel(this.deviceInfo,item)
  502. },
  503. /** 更新设备状态 */
  504. updateDeviceStatus(device) {
  505. },
  506. chooseItemData(data){
  507. if(this.deviceInfo.status != 3){
  508. uni.showToast({
  509. title:"设备暂未上线",
  510. icon:"error"
  511. })
  512. return;
  513. }
  514. this.columns = [data.enumList];
  515. this.show =data;
  516. },
  517. getDetail(){
  518. let self = this;
  519. getDetail(this.id).then(res=>{
  520. self.deviceInfo = res.data;
  521. self.location = {
  522. latitude: self.deviceInfo.latitude,
  523. longitude: self.deviceInfo.longitude};
  524. self.sysDept = self.deviceInfo.sysDept;
  525. self.parseSummay(res.data.summary)
  526. self.mqttSubscribe(res.data);
  527. self.getDeviceStatus();
  528. self.seeTime();
  529. });
  530. },
  531. getDeviceStatus(e){
  532. let self = this;
  533. if(!e){
  534. e = {index:0};
  535. }
  536. this.activeName = e.index;
  537. this.childId = this.summary[this.activeName].id;
  538. getDeviceStatus(this.id,this.childId).then(res=>{
  539. let data =res.data;
  540. this.deviceInfo = data;
  541. self.parseStatusData(data)
  542. });
  543. },
  544. parseEnumList(){
  545. let enumList = this.deviceInfo.enumList;
  546. for (let enumListElement of enumList) {
  547. let id = enumListElement.id;
  548. let isCommonKey = this.modelKey.some(res=>{
  549. return id === res;
  550. })
  551. if(isCommonKey){
  552. if(id.indexOf("LockControl") !=-1){
  553. let shadow = enumListElement.shadow;
  554. this.lock.item = enumListElement;
  555. if(shadow == 1){
  556. this.lock.value = true;
  557. this.lock.loading = false;
  558. }else if(shadow == 0){
  559. this.lock.value = false;
  560. this.lock.loading = false;
  561. }else{
  562. this.lock.loading = true;
  563. }
  564. }
  565. if(id.indexOf("PowerControl") !=-1){
  566. let shadow = enumListElement.shadow;
  567. this.power.item = enumListElement;
  568. if(shadow == 1){
  569. this.power.value = true;
  570. this.power.loading = false;
  571. }else if(shadow == 0){
  572. this.power.value = false;
  573. this.power.loading = false;
  574. }else{
  575. this.power.loading = true;
  576. }
  577. }
  578. }
  579. let shadow = enumListElement.shadow
  580. let valueList = enumListElement.enumList;
  581. for (let valueObj of valueList){
  582. if(valueObj.value == shadow){
  583. enumListElement.text = valueObj.text;
  584. }
  585. }
  586. }
  587. },
  588. parseStatusData(data){
  589. let arr = [];
  590. let arrayList = data.arrayList;
  591. arr = arr.concat(arrayList);
  592. let enumList = data.enumList;
  593. arr = arr.concat(enumList);
  594. let integerList = data.integerList;
  595. arr = arr.concat(integerList);
  596. let stringList = data.stringList;
  597. arr = arr.concat(stringList);
  598. this.inputProp = arr;
  599. let readOnlyList = data.readOnlyList;
  600. this.watchProp = readOnlyList;
  601. this.parseEnumList();
  602. },
  603. parseSummay(summary){
  604. let self = this;
  605. if(!summary){
  606. summary = "";
  607. }
  608. if(summary.length>0){
  609. this.summary = JSON.parse(summary);
  610. if(self.summary.length>0){
  611. this.oneToMul = true;
  612. }
  613. for (let i = 0; i < self.summary.length; i++) {
  614. self.summary[i].tabName = self.summary[i].id+"_"+self.summary[i].name
  615. }
  616. let childId = "";
  617. if(this.oneToMul){
  618. let info = self.summary[self.activeName];
  619. childId = info.id;
  620. }
  621. this.childId = childId;
  622. }else{
  623. this.summary = [{tabName:"详情查看"}]
  624. }
  625. },
  626. goDeviceDetail(id){
  627. uni.navigateTo({
  628. url: '/pages/device/detail/detail?id='+id
  629. });
  630. },
  631. async connectMqtt() {
  632. if (this.$mqttTool.client == null) {
  633. await this.$mqttTool.connect(this.vuex_token);
  634. }
  635. this.mqttCallback();
  636. this.startCheck();
  637. },
  638. /** Mqtt订阅主题 */
  639. mqttSubscribe(device) {
  640. // 订阅当前设备状态和实时监测
  641. let topicStatus = '/' + device.productId + '/' + device.serialNumber + '/status/post';
  642. let topicProperty = '/' + device.productId + '/' + device.serialNumber + '/property/post';
  643. let topicFunction = '/' + device.productId + '/' + device.serialNumber + '/function/post';
  644. let topics = [];
  645. topics.push(topicStatus);
  646. topics.push(topicProperty);
  647. topics.push(topicFunction);
  648. this.$mqttTool.subscribe(topics);
  649. },
  650. /** Mqtt取消订阅主题 */
  651. mqttUnSubscribe(device) {
  652. // 订阅当前设备状态和实时监测
  653. let topicStatus = '/' + device.productId + '/' + device.serialNumber + '/status/post';
  654. let topicProperty = '/' + device.productId + '/' + device.serialNumber + '/property/post';
  655. let topicFunction = '/' + device.productId + '/' + device.serialNumber + '/function/post';
  656. let topics = [];
  657. topics.push(topicStatus);
  658. topics.push(topicProperty);
  659. topics.push(topicFunction);
  660. console.log('取消订阅', topics);
  661. this.$mqttTool.unsubscribe(topics);
  662. },
  663. /* Mqtt回调处理 */
  664. mqttCallback() {
  665. this.$mqttTool.client.on('message', (topic, message, buffer) => {
  666. let topics = topic.split('/');
  667. let productId = topics[1];
  668. let deviceNum = topics[2];
  669. console.log('接收到内容:'+message);
  670. message = JSON.parse(message.toString());
  671. if (topics[3] == 'status') {
  672. console.log('接收到【设备状态-运行】主题:', topic);
  673. console.log('接收到【设备状态-运行】内容:', message);
  674. // 更新列表中设备的状态
  675. if (this.deviceInfo.serialNumber == deviceNum) {
  676. this.deviceInfo.status = message.status;
  677. this.deviceInfo.isShadow = message.isShadow;
  678. this.deviceInfo.rssi = message.rssi;
  679. this.updateDeviceStatus(this.deviceInfo);
  680. }
  681. }
  682. if (topics[3] == 'property' || topics[3] == 'function') {
  683. console.log('接收到【物模型】主题:', topic);
  684. console.log('接收到【物模型】内容:', message);
  685. if(this.oneToMul){
  686. let curTabId = this.summary[this.activeName].id;
  687. let msg = [];
  688. for (let i = 0; i < message.length; i++) {
  689. let curMsg = message[i];
  690. let id = curMsg.id;
  691. let ids = id.split("_");
  692. let value = curMsg.value;
  693. if(ids.length == 2){
  694. if(curTabId == ids[1]){
  695. msg.push({id:ids[0],value:value});
  696. }
  697. }
  698. }
  699. message = msg;
  700. }
  701. // 更新列表中设备的属性
  702. if (this.deviceInfo.serialNumber == deviceNum) {
  703. for (let j = 0; j < message.length; j++) {
  704. let isComplete = false;
  705. // 布尔类型
  706. for (let k = 0; k < this.deviceInfo.boolList.length && !isComplete; k++) {
  707. if (this.deviceInfo.boolList[k].id == message[j].id) {
  708. this.deviceInfo.boolList[k].shadow = message[j].value;
  709. isComplete = true;
  710. break;
  711. }
  712. }
  713. // 枚举类型
  714. for (let k = 0; k < this.deviceInfo.enumList.length && !isComplete; k++) {
  715. if (this.deviceInfo.enumList[k].id == message[j].id) {
  716. this.deviceInfo.enumList[k].shadow = message[j].value;
  717. isComplete = true;
  718. break;
  719. }
  720. }
  721. // 字符串类型
  722. for (let k = 0; k < this.deviceInfo.stringList.length && !isComplete; k++) {
  723. if (this.deviceInfo.stringList[k].id == message[j].id) {
  724. this.deviceInfo.stringList[k].shadow = message[j].value;
  725. isComplete = true;
  726. break;
  727. }
  728. }
  729. // 数组类型
  730. for (let k = 0; k < this.deviceInfo.arrayList.length && !isComplete; k++) {
  731. if (this.deviceInfo.arrayList[k].id == message[j].id) {
  732. this.deviceInfo.arrayList[k].shadow = message[j].value;
  733. isComplete = true;
  734. break;
  735. }
  736. }
  737. // 整数类型
  738. for (let k = 0; k < this.deviceInfo.integerList.length && !isComplete; k++) {
  739. if (this.deviceInfo.integerList[k].id == message[j].id) {
  740. this.deviceInfo.integerList[k].shadow = message[j].value;
  741. isComplete = true;
  742. break;
  743. }
  744. }
  745. // 小数类型
  746. for (let k = 0; k < this.deviceInfo.decimalList.length && !isComplete; k++) {
  747. if (this.deviceInfo.decimalList[k].id == message[j].id) {
  748. this.deviceInfo.decimalList[k].shadow = message[j].value;
  749. isComplete = true;
  750. break;
  751. }
  752. }
  753. // 监测数据
  754. for (let k = 0; k < this.deviceInfo.readOnlyList.length && !isComplete; k++) {
  755. if (this.deviceInfo.readOnlyList[k].id == message[j].id) {
  756. this.deviceInfo.readOnlyList[k].shadow = message[j].value;
  757. // 更新图表
  758. // for (let m = 0; m < this.monitorChart.length; m++) {
  759. // if (message[j].id == this.monitorChart[m].data.id) {
  760. // let data = [{
  761. // value: message[j].value,
  762. // name: this.monitorChart[m].data.name
  763. // }];
  764. // this.monitorChart[m].chart.setOption({
  765. // series: [{
  766. // data: data
  767. // }]
  768. // });
  769. // break;
  770. // }
  771. // }
  772. isComplete = true;
  773. break;
  774. }
  775. }
  776. }
  777. this.parseEnumList();
  778. }
  779. }
  780. });
  781. },
  782. /** 发布物模型 类型(1=属性,2=功能) */
  783. publishThingsModel(device, model) {
  784. // 获取缓存的Json物模型
  785. cacheJsonThingsModel(device.productId).then(response => {
  786. let thingsModel = JSON.parse(response.data);
  787. let type = 0;
  788. for (let i = 0; i < thingsModel.functions.length; i++) {
  789. if (model.id == thingsModel.functions[i].id) {
  790. type = 2;
  791. break;
  792. }
  793. }
  794. if (type == 0) {
  795. for (let i = 0; i < thingsModel.properties.length; i++) {
  796. if (model.id == thingsModel.properties[i].id) {
  797. type = 1;
  798. break;
  799. }
  800. }
  801. }
  802. if (type != 0) {
  803. this.mqttPublish(type, device, model);
  804. }
  805. });
  806. },
  807. notifyError(res){
  808. uni.showToast({
  809. title:res,
  810. icon:"error"
  811. })
  812. },
  813. notifySuccess(res){
  814. uni.showToast({
  815. title:res,
  816. icon:"success"
  817. })
  818. },
  819. /**
  820. * Mqtt发布消息
  821. * @type 类型(1=属性,2=功能,3=OTA升级,4=实时监测)
  822. * @device 设备
  823. * @model 物模型
  824. * */
  825. mqttPublish(type, device, model) {
  826. let topic = "";
  827. let message = ""
  828. let oneToMul = false;
  829. if(this.summary.length>0){
  830. oneToMul = true;
  831. }
  832. let modelId = model.id;
  833. if(oneToMul){
  834. let info = this.summary[this.activeName];
  835. let childId = info.id;
  836. modelId = modelId+"_"+childId;
  837. }
  838. if (type == 1) {
  839. if (device.status == 3) {
  840. // 属性,在线模式
  841. topic = "/" + device.productId + "/" + device.serialNumber + "/property-online/get";
  842. } else if (device.isShadow) {
  843. // 属性,离线模式
  844. topic = "/" + device.productId + "/" + device.serialNumber + "/property-offline/post";
  845. }
  846. message = '[{"id":"' + modelId + '","value":"' + model.shadow + '"}]';
  847. } else if (type == 2) {
  848. if (device.status == 3) {
  849. // 功能,在线模式
  850. topic = "/" + device.productId + "/" + device.serialNumber + "/function-online/get";
  851. } else if (device.isShadow) {
  852. // 功能,离线模式
  853. topic = "/" + device.productId + "/" + device.serialNumber + "/function-offline/post";
  854. }
  855. message = '[{"id":"' + modelId + '","value":"' + model.shadow + '"}]';
  856. } else if (type == 3) {
  857. // OTA升级
  858. topic = "/" + device.productId + "/" + device.serialNumber + "/ota/get";
  859. message = '{"version":' + this.firmware.version + ',"downloadUrl":"' + this.getDownloadUrl(this.firmware.filePath) + '"}';
  860. } else {
  861. return;
  862. }
  863. if (topic != "") {
  864. // 发布
  865. let self = this;
  866. this.$mqttTool.publish(topic, message, model.name).then(res => {
  867. this.notifySuccess(res);
  868. }).catch(res => {
  869. this.notifyError(res);
  870. });
  871. }
  872. },
  873. startCheck(){
  874. let self = this;
  875. this.checkTimer = setInterval(function (){
  876. self.sendHeart();
  877. },20000);
  878. },
  879. checkActive(){
  880. let self = this;
  881. setTimeout(function (){
  882. if(self.publishMsg){
  883. self.resetConn()
  884. }
  885. },3000);
  886. },
  887. sendHeart(){
  888. console.log("发送心跳")
  889. let device = this.deviceInfo;
  890. let self = this;
  891. let topic = "/property-offline/post";
  892. self.publishMsg = true;
  893. self.checkActive();
  894. this.$mqttTool.publish(topic, "ok", "heart").then(res => {
  895. self.publishMsg = false;
  896. }).catch(res => {
  897. self.publishMsg = false;
  898. });
  899. },
  900. resetConn(){
  901. console.log("检测异常,重连")
  902. this.$mqttTool.end();
  903. this.$mqttTool.client = null;
  904. this.connectMqtt();
  905. this.getDetail();
  906. }
  907. }
  908. }
  909. </script>
  910. <style>
  911. .header{
  912. width: 100%;
  913. background: white;
  914. padding:0px 20rpx;
  915. position: relative;
  916. }
  917. .content {
  918. display: flex;
  919. align-items: center;
  920. justify-content: center;
  921. }
  922. .logo {
  923. height: 200rpx;
  924. width: 200rpx;
  925. margin-top: 200rpx;
  926. margin-left: auto;
  927. margin-right: auto;
  928. margin-bottom: 50rpx;
  929. }
  930. .text-area {
  931. margin:10px;
  932. padding-bottom: 10px;
  933. justify-content: center;
  934. width: 100%;
  935. }
  936. .grid-text {
  937. font-size: 14px;
  938. color: #909399;
  939. padding: 10rpx 0 20rpx 0rpx;
  940. /* #ifndef APP-PLUS */
  941. box-sizing: border-box;
  942. /* #endif */
  943. }
  944. .title {
  945. font-size: 36rpx;
  946. color: #8f8f94;
  947. }
  948. .item{
  949. height: 80rpx;
  950. line-height: 80rpx;
  951. }
  952. .bg{
  953. position: relative;
  954. }
  955. .text-left{
  956. color: #8A92A5;
  957. }
  958. .text-right{
  959. color: #545454;
  960. }
  961. .prop-text{
  962. position: relative;
  963. margin:20rpx 0;
  964. font-size: 26rpx;
  965. }
  966. .prop-card-area{
  967. }
  968. .prop-card{
  969. width: 28%;
  970. height: 100rpx;
  971. text-align: center;
  972. display: inline-block;
  973. background: #F5FCFF;
  974. box-shadow: 0px 9rpx 8rpx 0px rgba(0,0,0,0.09);
  975. border-radius: 8rpx;
  976. margin:0px 15rpx;
  977. line-height: 48rpx;
  978. }
  979. .prop-card-top{
  980. color: #3E9CFC;
  981. }
  982. .prop-card-bottom{
  983. color: #8A92A5;
  984. }
  985. .tab-area {
  986. background: white;
  987. position: absolute;
  988. left: 0px;
  989. right: 0px;
  990. top:480rpx;
  991. bottom:-20px;
  992. min-height: 200rpx;
  993. box-shadow: 0rpx 5rpx 27rpx 0rpx rgba(195, 195, 195, 0.4);
  994. border-radius: 40rpx;
  995. overflow-y: auto;
  996. padding-bottom:80rpx;
  997. }
  998. .prop-item{
  999. justify-content:center;
  1000. position: relative;
  1001. border-bottom: 1px solid lightgray;
  1002. height: 80rpx;
  1003. margin:0 20rpx;
  1004. }
  1005. .prop-item-left{
  1006. position: absolute;
  1007. left:10rpx;
  1008. top:22rpx;
  1009. color: #545454;
  1010. }
  1011. .prop-item-right{
  1012. position: absolute;
  1013. right:10rpx;
  1014. top:10rpx;
  1015. }
  1016. input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{
  1017. color:#666;
  1018. font-size:12px;
  1019. }
  1020. </style>