detail.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  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. {{ deviceInfo.name }}
  11. </view>
  12. <view class="device-detail__body">
  13. <DeviceCard
  14. :dataSource="deviceInfo"
  15. :collectTime="time"
  16. :title="deviceInfo.name"
  17. :deviceType="deviceType"
  18. />
  19. <view class="tabs">
  20. <view class="tab-container" v-if="isShowTab">
  21. <view class="tab-item" :class="activeTab === 'pestAnalysis'?'active':''" @click="handleTabClick('pestAnalysis')" v-if="disableShow">
  22. 害虫分析
  23. </view>
  24. <view class="tab-item" :class="activeTab === 'viewImage'?'active':''" @click="handleTabClick('viewImage')">
  25. 查看图片
  26. </view>
  27. <view class="tab-item" :class="activeTab === 'deviceData'?'active':''" @click="handleTabClick('deviceData')">
  28. 设备数据
  29. </view>
  30. </view>
  31. <view class="select-timer-container">
  32. <view class="select-year">
  33. <view class="select-year-item" @click="showPicker = true">
  34. {{ currentYear }}
  35. <u-icon name="arrow-down" size="18" class="arrow-down"></u-icon>
  36. </view>
  37. </view>
  38. <view class="tabs-timer-container" @click="showCalendar">
  39. <view class="tabs-timer-item">
  40. {{startDate}}
  41. </view>
  42. <view class="tabs-timer-item">
  43. {{endDate}}
  44. </view>
  45. <u-icon name="calendar" size="36" class="calendar-icon"></u-icon>
  46. </view>
  47. </view>
  48. </view>
  49. <view v-if="activeTab === 'pestAnalysis'" class="tab-content">
  50. <PestDiscern
  51. :total="total"
  52. :pest_order="pest_order"
  53. />
  54. <PestEchart
  55. :pest_order="pest_order"
  56. @getInfo="getInfo"
  57. :endDate="endDate"
  58. :d_id="deviceInfo.d_id"
  59. :day="day"
  60. :pest="pest"
  61. />
  62. <PestArchive
  63. :pest_info="pestInfo"
  64. />
  65. <view v-if="!pests.length">
  66. <u-empty text="暂无数据"></u-empty>
  67. </view>
  68. </view>
  69. <view v-if="activeTab === 'viewImage'">
  70. <photoImage
  71. :disableShow="disableShow"
  72. :images="imageList"
  73. :pestList="pestList"
  74. @changeTab="changeTab"
  75. :currentYear="currentYear"
  76. :deviceInfo="deviceInfo"
  77. />
  78. </view>
  79. <view v-if="activeTab === 'deviceData'">
  80. <DeviceData
  81. :deviceStatic="deviceStatic"
  82. :deviceInfo="deviceInfo"
  83. :polylineList="polylineList"
  84. :deviceHistoryList="deviceHistoryList"
  85. :totalPage="totalPage"
  86. :currentPage="page"
  87. :page_size="page_size"
  88. @prevPage="prevPage"
  89. @nextPage="nextPage"
  90. />
  91. </view>
  92. </view>
  93. <u-calendar v-model="show" :mode="mode" range-color="#999" btn-type="success" active-bg-color="#0BBC58" range-bg-color="rgba(11,188,88,0.13)" @change="handleChange" :max-date="maxDate" :min-date="minDate"></u-calendar>
  94. <u-picker v-model="showPicker" mode="selector" :range="selectorRange" range-key="id" :default-selector="[0]" @confirm="confirmHandler"></u-picker>
  95. </view>
  96. </template>
  97. <script>
  98. import DeviceCard from './components/DeviceCard.vue';
  99. import PestDiscern from './components/pestDiscern.vue';
  100. import PestEchart from './components/pestEchart.vue';
  101. import PestArchive from './components/pestArchive.vue';
  102. import photoImage from './components/photoImage.vue';
  103. import DeviceData from './components/deviceData.vue';
  104. export default {
  105. components: {
  106. DeviceCard,
  107. PestDiscern,
  108. PestEchart,
  109. PestArchive,
  110. photoImage,
  111. DeviceData
  112. },
  113. data(){
  114. return {
  115. isShowTab:false,
  116. showPicker: false,
  117. disableShow: false,
  118. totalPage:0,
  119. currentYear: new Date().getFullYear(),
  120. selectorRange: [],
  121. maxDate: this.formatDate(new Date()),
  122. minDate: this.formatDate(new Date(new Date().getFullYear(), 0, 1)),
  123. show: false,
  124. // 当前日期向前推30天 格式2026-01-01 月份和日期小于10的时候前面加个0
  125. startDate: this.formatDate(new Date(new Date().getTime() - 30 * 24 * 60 * 60 * 1000)),
  126. endDate: this.formatDate(new Date()),
  127. mode: 'range',
  128. imageList: [],
  129. pest_order:{},
  130. deviceInfo: {},
  131. pestInfo: {},
  132. time: '',
  133. activeTab: 'pestAnalysis',
  134. deviceType: '',
  135. location: '',
  136. total: 0,
  137. day: [],
  138. pest: [],
  139. page:1,
  140. is_pest:'',
  141. page_size:24,
  142. pestList:[],
  143. pest_names:'',
  144. pests:[],
  145. polylineList:[],
  146. deviceHistoryList:[],
  147. deviceStatic:{}
  148. }
  149. },
  150. onLoad(options){
  151. this.deviceInfo = JSON.parse(options.info);
  152. this.getPestAnalysis();
  153. this.isShow();
  154. const currentYear = new Date().getFullYear();
  155. this.selectorRange = [];
  156. for(let i = 0;i<50;i++){
  157. const item = {
  158. label: currentYear - i,
  159. id: currentYear - i
  160. }
  161. this.selectorRange.push(item);
  162. }
  163. },
  164. methods: {
  165. isShow(){
  166. // disable == 0 或者 device_model == 15 表示不可以查看
  167. if(!this.deviceInfo.d_id){
  168. return false;
  169. }
  170. let showStatus = true;
  171. if(this.deviceInfo.device_model == 15 || this.deviceInfo.device_model == 12){
  172. showStatus = false;
  173. }
  174. if(showStatus){
  175. this.activeTab = 'pestAnalysis';
  176. }else{
  177. this.activeTab = 'deviceData';
  178. }
  179. this.initAction();
  180. this.isShowTab = showStatus;
  181. if(this.deviceInfo.disable == 0){
  182. this.disableShow = false;
  183. if(showStatus){
  184. this.activeTab = 'viewImage';
  185. this.handleTabClick('viewImage');
  186. }
  187. }else{
  188. this.disableShow = true;
  189. }
  190. },
  191. prevPage(e){
  192. if(e == 1){
  193. return
  194. }
  195. this.page = e;
  196. this.getDeviceHistoryData();
  197. },
  198. nextPage(e){
  199. if(e * this.page_size >= this.totalPage){
  200. return
  201. }
  202. this.page = e;
  203. this.getDeviceHistoryData();
  204. },
  205. changeTab(pestList){
  206. let pest_names = pestList.map(item => item.pest_name);
  207. this.pest_names = pest_names.join(',');
  208. this.initImageList();
  209. },
  210. confirmHandler(e){
  211. this.currentYear = this.selectorRange[e].id;
  212. if(this.currentYear == new Date().getFullYear()){
  213. // 结束日期为this.endDate的月份和日期加上选择的年份
  214. const timeDate = this.currentYear + '-' + this.endDate.split('-')[1] + '-' + this.endDate.split('-')[2];
  215. this.endDate = this.formatDate(new Date(timeDate));
  216. // 开始日期为结束日期前30天
  217. this.startDate = this.formatDate(new Date(new Date(this.endDate).getTime() - 30 * 24 * 60 * 60 * 1000));
  218. this.maxDate = this.formatDate(new Date());
  219. this.minDate = this.formatDate(new Date(new Date().getFullYear(), 0, 1));
  220. }else{
  221. // 结束日期为this.endDate的月份和日期加上选择的年份
  222. const timeDate = this.currentYear + '-' + this.endDate.split('-')[1] + '-' + this.endDate.split('-')[2];
  223. this.endDate = this.formatDate(new Date(timeDate));
  224. // 开始日期为结束日期前30天
  225. this.startDate = this.formatDate(new Date(new Date(this.endDate).getTime() - 30 * 24 * 60 * 60 * 1000));
  226. this.maxDate = this.formatDate(new Date(this.currentYear, 11, 31));
  227. this.minDate = this.formatDate(new Date(this.currentYear, 0, 1));
  228. }
  229. this.initAction();
  230. this.showPicker = false;
  231. },
  232. getInfo(info){
  233. this.pestInfo = info;
  234. },
  235. // 格式化日期为YYYY-MM-DD格式,月份和日期小于10时前面加0
  236. formatDate(date) {
  237. const year = date.getFullYear();
  238. const month = String(date.getMonth() + 1).padStart(2, '0');
  239. const day = String(date.getDate()).padStart(2, '0');
  240. return `${year}-${month}-${day}`;
  241. },
  242. handleChange(e){
  243. this.startDate = e.startDate;
  244. this.endDate = e.endDate;
  245. this.initAction();
  246. },
  247. showCalendar(){
  248. this.show = true;
  249. },
  250. handleBack() {
  251. uni.navigateBack({
  252. delta: 1
  253. });
  254. },
  255. initAction(){
  256. this.pest_order = {}
  257. if(this.activeTab === 'pestAnalysis'){
  258. this.getPestAnalysis();
  259. }else if(this.activeTab === 'viewImage'){
  260. this.initPest();
  261. this.initImageList();
  262. }else if(this.activeTab === 'deviceData'){
  263. this.getDeviceData();
  264. this.getPolylineData();
  265. this.getDeviceHistoryData();
  266. }
  267. },
  268. handleTabClick(tab) {
  269. this.activeTab = tab;
  270. this.initAction();
  271. },
  272. async getDeviceData(){
  273. const res = await this.$myRequest({
  274. url: '/api/api_gateway?method=forecast.worm_lamp.device_status_data',
  275. method: 'POST',
  276. data: {
  277. device_id: this.deviceInfo.id,
  278. },
  279. });
  280. this.deviceStatic = res;
  281. },
  282. async initImageList(){
  283. const res = await this.$myRequest({
  284. url: '/api/api_gateway?method=forecast.new_cbd.photo_list',
  285. method: 'POST',
  286. data: {
  287. page:this.page,
  288. page_size:this.page_size,
  289. device_id: this.deviceInfo.id,
  290. is_pest: this.is_pest,
  291. identify_model: 'B',
  292. pest_names: this.pest_names,
  293. time_begin: this.formatDate(new Date(this.startDate)) + ' 00:00:00',// 格式化开始时间YYYY-MM-DD HH:MM:SS
  294. time_end: this.formatDate(new Date(this.endDate)) + ' 23:59:59',// 格式化结束时间YYYY-MM-DD HH:MM:SS
  295. },
  296. });
  297. const data = res?.data || [];
  298. this.imageList = data
  299. },
  300. async initPest(){
  301. const res = await this.$myRequest({
  302. url: '/api/api_gateway?method=forecast.new_cbd.pest_type_list',
  303. method: 'POST',
  304. data:{
  305. page:1,
  306. page_size:999999,
  307. device_id: this.deviceInfo.id,
  308. identify_model: 'B',
  309. time_begin: this.formatDate(new Date(this.startDate)) + ' 00:00:00',// 格式化开始时间YYYY-MM-DD HH:MM:SS
  310. time_end: this.formatDate(new Date(this.endDate)) + ' 23:59:59',// 格式化结束时间YYYY-MM-DD HH:MM:SS
  311. },
  312. });
  313. const data = res?.data || [];
  314. this.pestList = data
  315. },
  316. async getPolylineData(){
  317. const res = await this.$myRequest({
  318. url: '/api/api_gateway?method=forecast.worm_lamp.device_polyline_data',
  319. method: 'POST',
  320. data: {
  321. device_type_id: this.deviceInfo.type_id,
  322. d_id: this.deviceInfo.d_id,
  323. start_time: new Date(this.startDate).getTime()/1000,// 转成毫秒
  324. end_time: new Date(this.endDate).getTime()/1000,// 转成毫秒
  325. },
  326. });
  327. const data = res || [];
  328. this.polylineList = data
  329. },
  330. async getDeviceHistoryData(){
  331. const res = await this.$myRequest({
  332. url: '/api/api_gateway?method=forecast.worm_lamp.device_history_data',
  333. method: 'POST',
  334. data: {
  335. device_type_id: this.deviceInfo.type_id,
  336. device_id: this.deviceInfo.id,
  337. start_time: new Date(this.startDate).getTime()/1000,
  338. end_time: new Date(this.endDate).getTime()/1000,
  339. page: this.page,
  340. page_size: this.page_size,
  341. },
  342. });
  343. const data = res?.data || [];
  344. this.totalPage = res.counts;
  345. this.deviceHistoryList = data
  346. },
  347. async getPestAnalysis(){
  348. const res = await this.$myRequest({
  349. url: '/api/api_gateway?method=forecast.cbd_analysis.analysis_pest_result',
  350. method: 'POST',
  351. data: {
  352. d_id: this.deviceInfo.d_id,
  353. start: this.startDate,
  354. end: this.endDate,
  355. model: 'B'
  356. },
  357. });
  358. const pest_order = res?.pest_order;
  359. this.getInfo({});
  360. let total = 0;
  361. this.pests = [];
  362. for(let key in pest_order){
  363. total += pest_order[key] || 0;
  364. this.pests.push({
  365. name: key,
  366. percent: (pest_order[key] / total) * 100
  367. })
  368. }
  369. this.pest_order = pest_order;
  370. this.day = res?.day || [];
  371. const pest = res?.pest || [];
  372. this.pest = pest;
  373. // pest.forEach(p =>{
  374. // for(let i = 0;i< p.length;i++){
  375. // this.pest.push(p[i]);
  376. // }
  377. // })
  378. this.total = total;
  379. }
  380. }
  381. }
  382. </script>
  383. <style scoped lang="scss">
  384. ::v-deep .u-calendar__action{
  385. display:flex;
  386. justify-content: space-between;
  387. }
  388. ::v-deep .u-hover-class{
  389. .u-calendar__content__item__inner{
  390. color:#aaa !important;
  391. }
  392. }
  393. .device-detail {
  394. display: flex;
  395. width: 100%;
  396. height: calc(100vh - 112rpx);
  397. padding-top: 112rpx;
  398. flex-direction: column;
  399. align-items: center;
  400. overflow-y: scroll;
  401. background: linear-gradient(180deg, #ffffff00 0%, #F5F6FA 23.64%, #F5F6FA 100%), linear-gradient(102deg, #BFEADD 6.77%, #B8F1E7 40.15%, #B9EEF5 84.02%);
  402. .device-detail__header {
  403. width: 100%;
  404. font-size: 28rpx;
  405. color: #042118;
  406. font-family: 'Source Han Sans CN VF';
  407. font-weight: 700;
  408. position: relative;
  409. text-align: center;
  410. .arrow-left {
  411. position: absolute;
  412. left: 32rpx;
  413. margin-right: 12rpx;
  414. }
  415. }
  416. .device-detail__body {
  417. width: calc(100% - 64rpx);
  418. margin: 0 auto;
  419. border-radius: 16rpx;
  420. overflow-x: hidden;
  421. overflow-y: auto;
  422. // 隐藏滚动条
  423. -ms-overflow-style: none;
  424. scrollbar-width: none;
  425. }
  426. .tab-content{
  427. width: 100%;
  428. padding-bottom: 32rpx;
  429. }
  430. .tabs {
  431. background: #ffffff;
  432. margin: 24rpx 0;
  433. border-radius: 16rpx;
  434. padding: 16rpx 0;
  435. padding-top: 0;
  436. .tab-container{
  437. display: flex;
  438. width: 100%;
  439. height: 88rpx;
  440. line-height: 88rpx;
  441. text-align: center;
  442. font-size: 28rpx;
  443. font-weight: 700;
  444. color: #042118;
  445. font-family: 'Source Han Sans CN VF';
  446. }
  447. .select-timer-container{
  448. display:flex;
  449. align-items: center;
  450. padding-left: 32rpx;
  451. .select-year{
  452. width: 110rpx;
  453. text-align: center;
  454. height: 64rpx;
  455. border-radius: 32rpx;
  456. font-family: 'Source Han Sans CN VF';
  457. line-height: 64rpx;
  458. background: #F1F4F8;
  459. padding: 0 32rpx;
  460. .select-year-item{
  461. color: #656565;
  462. font-size: 24rpx;
  463. display: flex;
  464. align-items: center;
  465. justify-content: space-around;
  466. }
  467. }
  468. }
  469. .tabs-timer-container{
  470. display: flex;
  471. align-items: center;
  472. width: calc(100% - 256rpx);
  473. height: 64rpx;
  474. line-height: 64rpx;
  475. text-align: center;
  476. font-size: 24rpx;
  477. font-weight: 500;
  478. font-family: 'Source Han Sans CN VF';
  479. border-radius: 32rpx;
  480. background: #F1F4F8;
  481. padding: 0 32rpx;
  482. color: #656565;
  483. margin: 16rpx;
  484. position: relative;
  485. .tabs-timer-item{
  486. width: 42%;
  487. color: #656565;
  488. text-align: center;
  489. font-family: "Source Han Sans CN VF";
  490. font-size: 24rpx;
  491. font-weight: 400;
  492. }
  493. .calendar-icon{
  494. margin-left: 24rpx;
  495. }
  496. }
  497. .tab-item {
  498. flex: 1;
  499. color: #999999;
  500. text-align: center;
  501. font-family: "Source Han Sans CN VF";
  502. font-size: 28rpx;
  503. font-weight: 400;
  504. }
  505. .active{
  506. position: relative;
  507. color: #303133;
  508. text-align: center;
  509. font-family: "Source Han Sans CN VF";
  510. font-size: 28rpx;
  511. font-weight: 700;
  512. &::after {
  513. content: '';
  514. position: absolute;
  515. bottom: 0;
  516. left: 50%;
  517. -webkit-transform: translateX(-50%);
  518. transform: translateX(-50%);
  519. width: 36rpx;
  520. height: 36rpx;
  521. border: 6rpx solid #0BBC58;
  522. border-radius: 50%;
  523. border-color: transparent;
  524. border-bottom-color: #0BBC58;
  525. }
  526. }
  527. }
  528. }
  529. </style>