detail.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  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. }
  185. }else{
  186. this.disableShow = true;
  187. }
  188. },
  189. prevPage(e){
  190. if(e == 1){
  191. return
  192. }
  193. this.page = e;
  194. this.getDeviceHistoryData();
  195. },
  196. nextPage(e){
  197. if(e * this.page_size >= this.totalPage){
  198. return
  199. }
  200. this.page = e;
  201. this.getDeviceHistoryData();
  202. },
  203. changeTab(pestList){
  204. let pest_names = pestList.map(item => item.pest_name);
  205. this.pest_names = pest_names.join(',');
  206. this.initImageList();
  207. },
  208. confirmHandler(e){
  209. this.currentYear = this.selectorRange[e].id;
  210. if(this.currentYear == new Date().getFullYear()){
  211. this.startDate = this.formatDate(new Date(new Date().getFullYear(), 0, 1));
  212. this.endDate = this.formatDate(new Date());
  213. this.maxDate = this.formatDate(new Date());
  214. this.minDate = this.formatDate(new Date(new Date().getFullYear(), 0, 1));
  215. }else{
  216. this.startDate = this.formatDate(new Date(this.currentYear, 0, 1));
  217. this.endDate = this.formatDate(new Date(this.currentYear, 11, 31));
  218. this.maxDate = this.formatDate(new Date(this.currentYear, 11, 31));
  219. this.minDate = this.formatDate(new Date(this.currentYear, 0, 1));
  220. }
  221. this.initAction();
  222. this.showPicker = false;
  223. },
  224. getInfo(info){
  225. this.pestInfo = info;
  226. },
  227. // 格式化日期为YYYY-MM-DD格式,月份和日期小于10时前面加0
  228. formatDate(date) {
  229. const year = date.getFullYear();
  230. const month = String(date.getMonth() + 1).padStart(2, '0');
  231. const day = String(date.getDate()).padStart(2, '0');
  232. return `${year}-${month}-${day}`;
  233. },
  234. handleChange(e){
  235. this.startDate = e.startDate;
  236. this.endDate = e.endDate;
  237. this.initAction();
  238. },
  239. showCalendar(){
  240. this.show = true;
  241. },
  242. handleBack() {
  243. uni.navigateBack({
  244. delta: 1
  245. });
  246. },
  247. initAction(){
  248. this.pest_order = {}
  249. if(this.activeTab === 'pestAnalysis'){
  250. this.getPestAnalysis();
  251. }else if(this.activeTab === 'viewImage'){
  252. this.initPest();
  253. this.initImageList();
  254. }else if(this.activeTab === 'deviceData'){
  255. this.getDeviceData();
  256. this.getPolylineData();
  257. this.getDeviceHistoryData();
  258. }
  259. },
  260. handleTabClick(tab) {
  261. this.activeTab = tab;
  262. this.initAction();
  263. },
  264. async getDeviceData(){
  265. const res = await this.$myRequest({
  266. url: '/api/api_gateway?method=forecast.worm_lamp.device_status_data',
  267. method: 'POST',
  268. data: {
  269. device_id: this.deviceInfo.id,
  270. },
  271. });
  272. this.deviceStatic = res;
  273. },
  274. async initImageList(){
  275. const res = await this.$myRequest({
  276. url: '/api/api_gateway?method=forecast.new_cbd.photo_list',
  277. method: 'POST',
  278. data: {
  279. page:this.page,
  280. page_size:this.page_size,
  281. device_id: this.deviceInfo.id,
  282. is_pest: this.is_pest,
  283. identify_model: 'B',
  284. pest_names: this.pest_names,
  285. time_begin: this.formatDate(new Date(this.startDate)) + ' 00:00:00',// 格式化开始时间YYYY-MM-DD HH:MM:SS
  286. time_end: this.formatDate(new Date(this.endDate)) + ' 23:59:59',// 格式化结束时间YYYY-MM-DD HH:MM:SS
  287. },
  288. });
  289. const data = res?.data || [];
  290. this.imageList = data
  291. },
  292. async initPest(){
  293. const res = await this.$myRequest({
  294. url: '/api/api_gateway?method=forecast.new_cbd.pest_type_list',
  295. method: 'POST',
  296. data:{
  297. page:1,
  298. page_size:999999,
  299. device_id: this.deviceInfo.id,
  300. identify_model: 'B',
  301. time_begin: this.formatDate(new Date(this.startDate)) + ' 00:00:00',// 格式化开始时间YYYY-MM-DD HH:MM:SS
  302. time_end: this.formatDate(new Date(this.endDate)) + ' 23:59:59',// 格式化结束时间YYYY-MM-DD HH:MM:SS
  303. },
  304. });
  305. const data = res?.data || [];
  306. this.pestList = data
  307. },
  308. async getPolylineData(){
  309. const res = await this.$myRequest({
  310. url: '/api/api_gateway?method=forecast.worm_lamp.device_polyline_data',
  311. method: 'POST',
  312. data: {
  313. device_type_id: this.deviceInfo.type_id,
  314. d_id: this.deviceInfo.d_id,
  315. start_time: new Date(this.startDate).getTime()/1000,// 转成毫秒
  316. end_time: new Date(this.endDate).getTime()/1000,// 转成毫秒
  317. },
  318. });
  319. const data = res || [];
  320. this.polylineList = data
  321. },
  322. async getDeviceHistoryData(){
  323. const res = await this.$myRequest({
  324. url: '/api/api_gateway?method=forecast.worm_lamp.device_history_data',
  325. method: 'POST',
  326. data: {
  327. device_type_id: this.deviceInfo.type_id,
  328. device_id: this.deviceInfo.id,
  329. start_time: new Date(this.startDate).getTime()/1000,
  330. end_time: new Date(this.endDate).getTime()/1000,
  331. page: this.page,
  332. page_size: this.page_size,
  333. },
  334. });
  335. const data = res?.data || [];
  336. this.totalPage = res.counts;
  337. this.deviceHistoryList = data
  338. },
  339. async getPestAnalysis(){
  340. const res = await this.$myRequest({
  341. url: '/api/api_gateway?method=forecast.cbd_analysis.analysis_pest_result',
  342. method: 'POST',
  343. data: {
  344. d_id: this.deviceInfo.d_id,
  345. start: this.startDate,
  346. end: this.endDate,
  347. model: 'B'
  348. },
  349. });
  350. const pest_order = res?.pest_order;
  351. this.getInfo({});
  352. let total = 0;
  353. this.pests = [];
  354. for(let key in pest_order){
  355. total += pest_order[key] || 0;
  356. this.pests.push({
  357. name: key,
  358. percent: (pest_order[key] / total) * 100
  359. })
  360. }
  361. this.pest_order = pest_order;
  362. this.day = res?.day || [];
  363. const pest = res?.pest || [];
  364. this.pest = pest;
  365. // pest.forEach(p =>{
  366. // for(let i = 0;i< p.length;i++){
  367. // this.pest.push(p[i]);
  368. // }
  369. // })
  370. this.total = total;
  371. }
  372. }
  373. }
  374. </script>
  375. <style scoped lang="scss">
  376. ::v-deep .u-calendar__action{
  377. display:flex;
  378. justify-content: space-between;
  379. }
  380. ::v-deep .u-hover-class{
  381. .u-calendar__content__item__inner{
  382. color:#aaa !important;
  383. }
  384. }
  385. .device-detail {
  386. display: flex;
  387. width: 100%;
  388. height: calc(100vh - 112rpx);
  389. padding-top: 112rpx;
  390. flex-direction: column;
  391. align-items: center;
  392. overflow-y: scroll;
  393. background: linear-gradient(180deg, #ffffff00 0%, #F5F6FA 23.64%, #F5F6FA 100%), linear-gradient(102deg, #BFEADD 6.77%, #B8F1E7 40.15%, #B9EEF5 84.02%);
  394. .device-detail__header {
  395. width: 100%;
  396. font-size: 28rpx;
  397. color: #999;
  398. color: #042118;
  399. font-family: 'Source Han Sans CN VF';
  400. font-weight: 700;
  401. position: relative;
  402. text-align: center;
  403. .arrow-left {
  404. position: absolute;
  405. left: 32rpx;
  406. margin-right: 12rpx;
  407. }
  408. }
  409. .device-detail__body {
  410. width: calc(100% - 64rpx);
  411. margin: 0 auto;
  412. border-radius: 16rpx;
  413. overflow-x: hidden;
  414. overflow-y: auto;
  415. // 隐藏滚动条
  416. -ms-overflow-style: none;
  417. scrollbar-width: none;
  418. }
  419. .tab-content{
  420. width: 100%;
  421. padding-bottom: 32rpx;
  422. }
  423. .tabs {
  424. background: #ffffff;
  425. margin: 24rpx 0;
  426. border-radius: 16rpx;
  427. padding: 16rpx 0;
  428. padding-top: 0;
  429. .tab-container{
  430. display: flex;
  431. width: 100%;
  432. height: 88rpx;
  433. line-height: 88rpx;
  434. text-align: center;
  435. font-size: 28rpx;
  436. font-weight: 700;
  437. color: #042118;
  438. font-family: 'Source Han Sans CN VF';
  439. }
  440. .select-timer-container{
  441. display:flex;
  442. align-items: center;
  443. padding-left: 32rpx;
  444. .select-year{
  445. width: 110rpx;
  446. text-align: center;
  447. height: 64rpx;
  448. border-radius: 32rpx;
  449. font-family: 'Source Han Sans CN VF';
  450. line-height: 64rpx;
  451. background: #F1F4F8;
  452. padding: 0 32rpx;
  453. .select-year-item{
  454. color: #656565;
  455. font-size: 24rpx;
  456. display: flex;
  457. align-items: center;
  458. justify-content: space-around;
  459. }
  460. }
  461. }
  462. .tabs-timer-container{
  463. display: flex;
  464. align-items: center;
  465. width: calc(100% - 256rpx);
  466. height: 64rpx;
  467. line-height: 64rpx;
  468. text-align: center;
  469. font-size: 24rpx;
  470. font-weight: 500;
  471. font-family: 'Source Han Sans CN VF';
  472. border-radius: 32rpx;
  473. background: #F1F4F8;
  474. padding: 0 32rpx;
  475. color: #656565;
  476. margin: 16rpx;
  477. position: relative;
  478. .tabs-timer-item{
  479. width: 42%;
  480. color: #656565;
  481. text-align: center;
  482. font-family: "Source Han Sans CN VF";
  483. font-size: 24rpx;
  484. font-weight: 400;
  485. }
  486. .calendar-icon{
  487. margin-left: 24rpx;
  488. }
  489. }
  490. .tab-item {
  491. flex: 1;
  492. color: #999999;
  493. text-align: center;
  494. font-family: "Source Han Sans CN VF";
  495. font-size: 28rpx;
  496. font-weight: 400;
  497. }
  498. .active{
  499. position: relative;
  500. color: #303133;
  501. text-align: center;
  502. font-family: "Source Han Sans CN VF";
  503. font-size: 28rpx;
  504. font-weight: 700;
  505. &::after {
  506. content: '';
  507. position: absolute;
  508. bottom: 0;
  509. left: 50%;
  510. -webkit-transform: translateX(-50%);
  511. transform: translateX(-50%);
  512. width: 36rpx;
  513. height: 36rpx;
  514. border: 6rpx solid #0BBC58;
  515. border-radius: 50%;
  516. border-color: transparent;
  517. border-bottom-color: #0BBC58;
  518. }
  519. }
  520. }
  521. }
  522. </style>