deviceControl2.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. <template>
  2. <view class="device-detail">
  3. <view class="device-detail__header">
  4. <u-icon
  5. size="36"
  6. class="arrow-left"
  7. name="arrow-left"
  8. @click="handleBack"
  9. ></u-icon>
  10. {{ title }}
  11. </view>
  12. <u-select v-model="show" :list="list" @confirm="confirmHandler"></u-select>
  13. <view class="device-detail__body">
  14. <view class="tabs">
  15. <view class="tab-container">
  16. <view class="tab-item" v-if="myuser_type" :class="activeTab === 'pestAnalysis'?'active':''" @click="handleTabClick('pestAnalysis')">
  17. 管理员
  18. </view>
  19. <view class="tab-item" :class="activeTab === 'viewImage'?'active':''" @click="handleTabClick('viewImage')">
  20. 设置
  21. </view>
  22. </view>
  23. </view>
  24. <view class="device-detail-content" v-if="activeTab === 'pestAnalysis'">
  25. <view class="device-detail-item">
  26. <text class="device-detail-label">识别种类</text>
  27. <view class="device-detail-btn-container">
  28. <view class="device-detail-btns" @click="show2 = true" size="mini">{{getSelectPest(selectPest)}}</view>
  29. <view class="device-detail-btn" @click="setBindPest">{{selectPest.pest_id == '' ? '恢复默认' : '绑定设备'}}</view>
  30. </view>
  31. </view>
  32. <view class="device-detail-item">
  33. <text class="device-detail-label">联网模块</text>
  34. <view class="device-detail-btn-container">
  35. <view class="device-detail-btn" @click="setDeviceContorl('reboot')">数据单元重启</view>
  36. <view class="device-detail-btn" @click="setDeviceContorl('upgradeApp')">数据单元升级</view>
  37. </view>
  38. </view>
  39. <view class="device-detail-item">
  40. <text class="device-detail-label">设备操作</text>
  41. <view class="device-detail-btn-container">
  42. <view class="device-detail-btn" @click="setDeviceContorl('takephoto')">测试拍照</view>
  43. <view class="device-detail-btn" @click="setDeviceContorl('getLocation')">获取定位</view>
  44. </view>
  45. </view>
  46. </view>
  47. <view class="device-detail-content" style="padding-bottom: 50rpx" v-if="activeTab === 'viewImage'">
  48. <view class="device-detail-viewImage">
  49. <text class="device-detail-label">拍照频率(min):</text>
  50. <u-input
  51. v-model="equipContrlForm.takePhotoIntervalMinutes"
  52. type="number"
  53. :border="false"
  54. height="40"
  55. style="width: 100rpx"
  56. auto-height="true"
  57. input-align="right"
  58. />
  59. </view>
  60. <view class="device-detail-viewImage">
  61. <text class="device-detail-label">控制模式:</text>
  62. <text @click="show = true">{{ formatCtrlMode(equipContrlForm.ctrlMode) }}</text>
  63. </view>
  64. <view class="device-detail-viewImage">
  65. <text class="device-detail-label">时控时长:</text>
  66. <text @click="show1 = true">{{ fromTime(equipContrlForm.startHour) }} - {{ fromTime(equipContrlForm.endHour) }}</text>
  67. </view>
  68. </view>
  69. </view>
  70. <view class="device-detail-btn-footer" v-if="activeTab === 'viewImage'">
  71. <view class="device-detail-btn" @click="saveSettings">保存</view>
  72. </view>
  73. <u-select v-model="show1" mode="mutil-column" :list="list2" @confirm="confirmDateHandler"></u-select>
  74. <u-select v-model="show2" :list="list3" @confirm="confirmDateHandler2"></u-select>
  75. </view>
  76. </template>
  77. <script>
  78. export default {
  79. data(){
  80. return {
  81. show: false,
  82. show1: false,
  83. show2: false,
  84. list2:[],
  85. list3:[],
  86. list: [
  87. { value: '0', label: '光控模式' },
  88. { value: '1', label: '时控模式' },
  89. ],
  90. selectPest: {},
  91. value: 10,
  92. cmd1: '0001',
  93. imageList: [],
  94. equipContrlForm: {},
  95. time: '',
  96. activeTab: 'pestAnalysis',
  97. title: '设置控制',
  98. deviceType: '',
  99. location: '',
  100. d_id: '',
  101. checked: false,
  102. sliderField: '',
  103. sliderMin: 0,
  104. sliderMax: 100,
  105. myuser_type: false,
  106. sliderRect: null
  107. }
  108. },
  109. onLoad(options){
  110. this.d_id = options.d_id
  111. const time1 = [];
  112. const time2 = [];
  113. for(let i = 0;i< 24;i++){
  114. let j = i;
  115. if(i < 10){
  116. j = '0' + i
  117. }
  118. time1.push({
  119. value: i,
  120. label: j + ':00'
  121. })
  122. time2.push({
  123. value: i,
  124. label: j + ':00'
  125. })
  126. }
  127. uni.getStorage({
  128. key:"myuser_type",
  129. success:(res)=>{
  130. if(Number(res.data) == 1){
  131. this.myuser_type = true
  132. this.activeTab = 'pestAnalysis'
  133. }else{
  134. this.activeTab = 'viewImage'
  135. }
  136. }
  137. })
  138. this.list2 = [time1, time2];
  139. this.deviceType = options.device_type
  140. this.getControlDeviceConfigInfo()
  141. this.getFollowPest()
  142. this.getAllPestList()
  143. },
  144. methods: {
  145. getSelectPest(selectPest){
  146. if(selectPest?.pest_name == '清空' || !selectPest?.pest_name){
  147. return '请选择识别种类'
  148. }else{
  149. return selectPest.pest_name
  150. }
  151. },
  152. async getAllPestList() {
  153. const res = await this.$myRequest({
  154. url: '/api/api_gateway?method=forecast.pest_info.pest_dict',
  155. data: {
  156. type_name: 1
  157. }
  158. })
  159. const pestList = [];
  160. for(let pest in res){
  161. pestList.push({
  162. value: pest,
  163. label: res[pest]
  164. })
  165. }
  166. pestList.unshift({
  167. value: '',
  168. label: '清空'
  169. })
  170. this.list3 = pestList
  171. },
  172. formatCtrlMode(ctrlMode){
  173. if(ctrlMode == 1){
  174. return '时控模式'
  175. }else if(ctrlMode == 0){
  176. return '光控模式'
  177. }
  178. return '请选择控制模式'
  179. },
  180. getProgressWidth(value, min, max) {
  181. if (max === min) return 0
  182. return ((value - min) / (max - min)) * 100
  183. },
  184. onSliderTouchStart(e, field, min, max) {
  185. this.sliderField = field
  186. this.sliderMin = min
  187. this.sliderMax = max
  188. const query = uni.createSelectorQuery().in(this)
  189. query.select('.custom-progress').boundingClientRect(rect => {
  190. this.sliderRect = rect
  191. }).exec()
  192. this.updateSliderValue(e.touches[0].clientX, field, min, max)
  193. },
  194. onSliderTouchMove(e, min, max) {
  195. if (!this.sliderField || !this.sliderRect) return
  196. this.updateSliderValue(e.touches[0].clientX, this.sliderField, min, max)
  197. },
  198. onSliderTouchEnd() {
  199. this.sliderField = ''
  200. },
  201. onSliderTap(e, field, min, max) {
  202. const query = uni.createSelectorQuery().in(this)
  203. query.select('.custom-progress').boundingClientRect(rect => {
  204. this.sliderRect = rect
  205. this.updateSliderValue(e.detail.x + rect.left, field, min, max)
  206. }).exec()
  207. },
  208. updateSliderValue(clientX, field, min, max) {
  209. if (!this.sliderRect) return
  210. let ratio = (clientX - this.sliderRect.left) / this.sliderRect.width
  211. ratio = Math.max(0, Math.min(1, ratio))
  212. const value = Math.round(min + ratio * (max - min))
  213. this.$set(this.equipContrlForm, field, value)
  214. },
  215. fromTime(time){
  216. if(time < 10){
  217. time = '0' + time + ':00'
  218. }else{
  219. time = time + ':00'
  220. }
  221. return time
  222. },
  223. confirmDateHandler2(e){
  224. console.log(e,'e')
  225. this.show2 = false
  226. this.selectPest.pest_id = e[0].value
  227. this.selectPest.pest_name = e[0].label
  228. },
  229. confirmDateHandler(e){
  230. console.log(e,'e')
  231. this.show1 = false
  232. this.equipContrlForm.startHour = e[0].value
  233. this.equipContrlForm.endHour = e[1].value
  234. },
  235. confirmHandler(item){
  236. this.equipContrlForm.ctrlMode = item[0].value
  237. },
  238. formart(cmd1){
  239. let text = ''
  240. this.list.forEach(item => {
  241. if(item.value === cmd1){
  242. text = item.label
  243. }
  244. })
  245. return text
  246. },
  247. handleBack() {
  248. uni.navigateBack({
  249. delta: 1
  250. });
  251. },
  252. handleTabClick(tab) {
  253. this.activeTab = tab;
  254. },
  255. async getFollowPest(){
  256. const res = await this.$myRequest({
  257. url: '/api/api_gateway?method=forecast.new_cbd.get_follow_pest',
  258. data: {
  259. d_id: this.d_id,
  260. }
  261. })
  262. if (res) {
  263. this.selectPest = res?.pest_list[0] || {}
  264. } else {
  265. this.showToast({
  266. title: '获取绑定设备失败',
  267. icon: 'error',
  268. });
  269. }
  270. },
  271. setBindPest(){
  272. let pest_data = {}
  273. if(this.selectPest.pest_id){
  274. pest_data = JSON.stringify([{
  275. pest_id: this.selectPest.pest_id,
  276. pest_name: this.selectPest.pest_name,
  277. }]);
  278. }else{
  279. pest_data = ''
  280. }
  281. const data = {
  282. d_id: this.d_id,
  283. disable: 0,
  284. pest_data
  285. }
  286. this.$myRequest({
  287. url: '/api/api_gateway?method=forecast.new_cbd.modify_follow_pest',
  288. method: 'POST',
  289. data
  290. }).then(res => {
  291. if (res) {
  292. uni.showToast({
  293. title: '绑定设备成功!',
  294. icon: 'success',
  295. });
  296. } else {
  297. uni.showToast({
  298. title: '绑定设备失败',
  299. icon: 'error',
  300. });
  301. }
  302. })
  303. },
  304. async setDeviceContorl(type){
  305. const data = {
  306. device_type_id: this.deviceType,
  307. d_id: this.d_id,
  308. cmd: type,
  309. }
  310. if(type === 'imei'){
  311. data.imei = this.equipContrlForm.imei
  312. }else{
  313. delete data.imei
  314. }
  315. this.$myRequest({
  316. url: '/api/api_gateway?method=forecast.send_control.admin_device_control',
  317. method: 'POST',
  318. data
  319. }).then(res => {
  320. if (res) {
  321. uni.showToast({
  322. title: '设备控制修改成功!',
  323. icon: 'success',
  324. });
  325. } else {
  326. uni.showToast({
  327. title: '设备控制修改失败',
  328. icon: 'error',
  329. });
  330. }
  331. });
  332. },
  333. saveSettings(){
  334. let newForm = Object.assign({}, this.equipContrlForm) // 深拷贝
  335. this.$myRequest({
  336. url: '/api/api_gateway?method=new_gateway.device_info.update_device_config',
  337. method: 'POST',
  338. data: {
  339. device_type_id: this.deviceType,
  340. id: this.d_id,
  341. ...newForm,
  342. }
  343. }).then(res => {
  344. if (res) {
  345. // 设备控制修改成功
  346. uni.showToast({
  347. title: '设备控制修改成功!',
  348. icon: 'success',
  349. });
  350. } else {
  351. uni.showToast({
  352. title: '设备控制修改失败',
  353. icon: 'error',
  354. });
  355. }
  356. })
  357. },
  358. async getControlDeviceConfigInfo(){
  359. const res = await this.$myRequest({
  360. url: '/api/api_gateway?method=new_gateway.device_info.get_device_config',
  361. method: 'POST',
  362. data: {
  363. device_type_id: this.deviceType,
  364. id: this.d_id,
  365. },
  366. });
  367. console.log(res,'resresres')
  368. if(!res?.startHour){
  369. res.startHour = '0'
  370. }
  371. if(!res?.endHour){
  372. res.endHour = '0'
  373. }
  374. this.equipContrlForm = res
  375. },
  376. }
  377. }
  378. </script>
  379. <style scoped lang="scss">
  380. .device-detail {
  381. display: flex;
  382. width: 100%;
  383. height: calc(100vh - 112rpx);
  384. padding-top: 112rpx;
  385. flex-direction: column;
  386. align-items: center;
  387. background: linear-gradient(180deg, #ffffff00 0%, #F5F6FA 23.64%, #F5F6FA 100%), linear-gradient(102deg, #BFEADD 6.77%, #B8F1E7 40.15%, #B9EEF5 84.02%);
  388. .device-detail__header {
  389. width: 100%;
  390. font-size: 28rpx;
  391. color: #999;
  392. color: #042118;
  393. font-family: 'Source Han Sans CN VF';
  394. font-weight: 700;
  395. position: relative;
  396. text-align: center;
  397. .arrow-left {
  398. position: absolute;
  399. left: 32rpx;
  400. margin-right: 12rpx;
  401. }
  402. }
  403. .device-detail__body {
  404. width: calc(100% - 64rpx);
  405. margin: 0 auto;
  406. border-radius: 16rpx;
  407. overflow-x: hidden;
  408. overflow-y: auto;
  409. // 隐藏滚动条
  410. -ms-overflow-style: none;
  411. scrollbar-width: none;
  412. }
  413. .tabs {
  414. margin: 24rpx 0;
  415. border-radius: 16rpx;
  416. padding: 16rpx 0;
  417. padding-top: 0;
  418. .tab-container{
  419. display: flex;
  420. width: 100%;
  421. height: 88rpx;
  422. line-height: 88rpx;
  423. text-align: center;
  424. font-size: 28rpx;
  425. font-weight: 700;
  426. color: #042118;
  427. font-family: 'Source Han Sans CN VF';
  428. }
  429. .tab-item {
  430. margin-right: 40rpx;
  431. color:#999999;
  432. }
  433. .active{
  434. position: relative;
  435. color: #303133;
  436. text-align: center;
  437. font-family: "Source Han Sans CN VF";
  438. font-size: 28rpx;
  439. font-weight: 700;
  440. &::after {
  441. content: '';
  442. position: absolute;
  443. bottom: 10rpx;
  444. left: 50%;
  445. transform: translateX(-50%);
  446. width: 100%;
  447. height: 36rpx;
  448. border-bottom: 6rpx solid #303133;
  449. }
  450. }
  451. }
  452. .device-detail-content{
  453. display: flex;
  454. padding: 24rpx 32rpx;
  455. flex-direction: column;
  456. align-items: flex-start;
  457. gap: 20rpx;
  458. border-radius: 16rpx;
  459. background: #FFF;
  460. .device-detail-item{
  461. .device-detail-label{
  462. color: #303133;
  463. font-family: "Source Han Sans CN VF";
  464. font-size: 28rpx;
  465. font-weight: 400;
  466. }
  467. .device-detail-btn-container{
  468. display: flex;
  469. gap: 24rpx;
  470. margin-top: 12rpx;
  471. }
  472. .device-detail-btns{
  473. display: flex;
  474. padding: 16rpx 24rpx;
  475. justify-content: center;
  476. align-items: center;
  477. gap: 16rpx;
  478. border: 1rpx solid #0BBC58;
  479. border-radius: 16rpx;
  480. color: #0BBC58;
  481. font-family: "Source Han Sans CN VF";
  482. font-size: 24rpx;
  483. font-weight: 400;
  484. }
  485. .device-detail-btn{
  486. display: flex;
  487. padding: 16rpx 24rpx;
  488. justify-content: center;
  489. align-items: center;
  490. gap: 16rpx;
  491. border-radius: 16rpx;
  492. background: #0BBC58;
  493. color: #ffffff;
  494. font-family: "Source Han Sans CN VF";
  495. font-size: 24rpx;
  496. font-weight: 400;
  497. }
  498. .force-btn{
  499. background: #FB4E52;
  500. }
  501. }
  502. .device-detail-viewImage{
  503. width: 100%;
  504. display: flex;
  505. justify-content: space-between;
  506. align-items: center;
  507. padding-bottom: 20rpx;
  508. }
  509. }
  510. .slider-container{
  511. position: relative;
  512. margin: 10rpx 0 20rpx 0;
  513. .slider-min-value{
  514. position: absolute;
  515. left: 0;
  516. top: -50rpx;
  517. }
  518. .slider{
  519. width: 600rpx;
  520. }
  521. .slider-max-value{
  522. position: absolute;
  523. right: 0;
  524. top: -50rpx;
  525. }
  526. }
  527. .custom-progress{
  528. width: 600rpx;
  529. padding: 0;
  530. .progress-track{
  531. position: relative;
  532. height: 12rpx;
  533. background-color: #ebedf0;
  534. border-radius: 6rpx;
  535. }
  536. .progress-fill{
  537. position: absolute;
  538. left: 0;
  539. top: 0;
  540. height: 100%;
  541. background-color: #0BBC58;
  542. border-radius: 6rpx;
  543. transition: width 0.2s;
  544. }
  545. .progress-thumb{
  546. position: absolute;
  547. top: 50%;
  548. width: 28rpx;
  549. height: 28rpx;
  550. margin-left: -14rpx;
  551. margin-top: -14rpx;
  552. border-radius: 50%;
  553. background-color: #fff;
  554. box-shadow: 0 1px 4px rgba(0,0,0,0.3);
  555. transition: left 0.2s;
  556. }
  557. }
  558. .device-detail-btn-footer{
  559. position: fixed;
  560. bottom: 0;
  561. left: 0;
  562. width: 100%;
  563. height: 112rpx;
  564. line-height: 112rpx;
  565. text-align: center;
  566. font-size: 28rpx;
  567. font-weight: 700;
  568. color: #042118;
  569. font-family: 'Source Han Sans CN VF';
  570. background: #ffffff;
  571. display: flex;
  572. justify-content: center;
  573. align-items: center;
  574. .device-detail-btn{
  575. display: flex;
  576. height: 100rpx;
  577. width: 90%;
  578. margin: 0 auto;
  579. padding: 10rpx 20rpx;
  580. justify-content: center;
  581. align-items: center;
  582. gap: 8rpx;
  583. border-radius: 16rpx;
  584. background:#0BBC58;
  585. color: #ffffff;
  586. text-align: center;
  587. font-family: "Source Han Sans CN VF";
  588. font-size: 14px;
  589. font-style: normal;
  590. font-weight: 500;
  591. }
  592. }
  593. }
  594. </style>