detail.vue 15 KB

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