pestEchart.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. <template>
  2. <view class="pest-echart">
  3. <view class="pest-echart__header">
  4. <view class="tab-list">
  5. <view
  6. v-for="(tab, index) in tabs"
  7. :key="index"
  8. class="tab-item"
  9. :class="{ active: activeTab === index }"
  10. @click="switchTab(index)"
  11. >
  12. {{ tab.name }}
  13. </view>
  14. </view>
  15. </view>
  16. <view class="pest-echart__content" v-if="dayData.length">
  17. <!-- 三个关键时期 -->
  18. <view class="period-section">
  19. <view class="period-item">
  20. <view class="period-label">始见期</view>
  21. <view class="period-value">{{ periodData.firstDate }}</view>
  22. </view>
  23. <view class="period-item">
  24. <view class="period-label">高峰期</view>
  25. <view class="period-value">{{ periodData.peakDate }}</view>
  26. </view>
  27. <view class="period-item">
  28. <view class="period-label">终见期</view>
  29. <view class="period-value">{{ periodData.lastDate }}</view>
  30. </view>
  31. </view>
  32. <!-- 图表区域 -->
  33. <view class="chart-container">
  34. <canvas
  35. canvas-id="pestChart"
  36. id="pestChart"
  37. class="charts"
  38. :style="{'width':cWidth*pixelRatio+'px','height':cHeight*pixelRatio+'px', 'transform': 'scale('+(1/pixelRatio)+')','margin-left':-cWidth*(pixelRatio-1)/2+'px','margin-top':-cHeight*(pixelRatio-1)/2+'px'}"
  39. @touchstart="touchChart($event)"
  40. @touchmove="moveChart($event)"
  41. @touchend="touchEndChart($event)"
  42. disable-scroll=true
  43. ></canvas>
  44. </view>
  45. </view>
  46. </view>
  47. </template>
  48. <script>
  49. import uCharts from '../../../components/js_sdk/u-charts/u-charts/u-charts.js';
  50. let chartInstance = null;
  51. export default {
  52. name: 'PestEchart',
  53. props:{
  54. pest_order:{
  55. type: Object,
  56. default: () => ({})
  57. },
  58. endDate: {
  59. type: String,
  60. default: ''
  61. },
  62. d_id: {
  63. type: String | Number,
  64. default: ''
  65. },
  66. day:{
  67. type: Array,
  68. default: () => []
  69. },
  70. pest:{
  71. type: Array,
  72. default: () => []
  73. },
  74. },
  75. data() {
  76. return {
  77. tabs: [],
  78. currentPest:'',
  79. activeTab: 0,
  80. dayData:[],
  81. // 三个关键时期数据
  82. periodData: {
  83. firstDate: '-',
  84. peakDate: '-',
  85. lastDate: '-'
  86. },
  87. chartData: {},
  88. // canvas 尺寸配置
  89. cWidth: 650,
  90. cHeight: 400,
  91. pixelRatio: 1,
  92. };
  93. },
  94. watch:{
  95. pest_order:{
  96. handler(val){
  97. this.tabs = [];
  98. for(let key in val){
  99. this.tabs.push({
  100. name: key,
  101. })
  102. }
  103. const name = this.tabs[0]?.name;
  104. this.currentPest = name;
  105. if(this.currentPest){
  106. this.getPestNameDetail(name);
  107. this.setChartData();
  108. }
  109. },
  110. deep: true
  111. },
  112. d_id:{
  113. handler(val){
  114. val && this.setChartData();
  115. },
  116. deep: true
  117. },
  118. endDate:{
  119. handler(val){
  120. val && this.setChartData();
  121. },
  122. deep: true
  123. },
  124. day:{
  125. handler(){
  126. this.initChart();
  127. },
  128. deep: true
  129. },
  130. },
  131. mounted() {
  132. this.cWidth = uni.upx2px(650);
  133. this.cHeight = uni.upx2px(400);
  134. this.pixelRatio = uni.getSystemInfoSync().pixelRatio;
  135. },
  136. methods: {
  137. async getPestNameDetail(name){
  138. const res = await this.$myRequest({
  139. url: '/api/pest_name_detail',
  140. method: 'POST',
  141. data: {
  142. name
  143. },
  144. });
  145. this.deviceInfo = res
  146. this.$emit('getInfo',res)
  147. },
  148. async setChartData(){
  149. if(!this.currentPest){
  150. return;
  151. }
  152. if(!this.d_id || !this.endDate){
  153. return;
  154. }
  155. const res = await this.$myRequest({
  156. url: '/api/api_gateway?method=forecast.cbd_analysis.pest_predict_time',
  157. method: 'POST',
  158. data: {
  159. model:'B',
  160. d_id: this.d_id,
  161. year: this.endDate.split('-')[0],
  162. pest: this.currentPest,
  163. },
  164. });
  165. this.periodData = {
  166. firstDate: res[0][0],
  167. peakDate: res[1][0],
  168. lastDate: res[2][0],
  169. }
  170. },
  171. initChart() {
  172. this.$nextTick(() => {
  173. setTimeout(() => {
  174. this.drawChart();
  175. }, 100);
  176. });
  177. },
  178. drawChart() {
  179. const dayData = this.day || [];
  180. this.dayData = dayData;
  181. const pestData = (this.pest || []).map(item => Number(item) || 0);
  182. const ctx = uni.createCanvasContext('pestChart', this);
  183. chartInstance = new uCharts({
  184. context: ctx,
  185. type: 'line',
  186. fontSize: 11,
  187. legend: {
  188. show: false
  189. },
  190. background: '#FFFFFF',
  191. pixelRatio: this.pixelRatio,
  192. animation: true,
  193. dataLabel: false,
  194. categories: dayData,
  195. series: [{
  196. name: '虫量',
  197. data: pestData
  198. }],
  199. color: ['#0085FF'],
  200. xAxis: {
  201. disableGrid: false,
  202. boundaryGap: 'justify',
  203. axisLine: true,
  204. lineColor: '#CCCCCC',
  205. fontColor: '#999999'
  206. },
  207. yAxis: {
  208. min: 0,
  209. splitNumber: 4,
  210. axisLine: true,
  211. lineColor: '#CCCCCC',
  212. fontColor: '#999999',
  213. gridType: 'dash',
  214. gridColor: '#E5E5E5'
  215. },
  216. width: this.cWidth * this.pixelRatio,
  217. height: this.cHeight * this.pixelRatio,
  218. extra: {
  219. line: {
  220. type: 'curve',
  221. width: 2,
  222. activeType: 'hollow'
  223. },
  224. tooltip: {
  225. showBox: true,
  226. bgOpacity: 0.7
  227. }
  228. }
  229. });
  230. },
  231. touchChart(e) {
  232. if (chartInstance) {
  233. chartInstance.scrollStart(e);
  234. }
  235. },
  236. moveChart(e) {
  237. if (chartInstance) {
  238. chartInstance.scroll(e);
  239. }
  240. },
  241. touchEndChart(e) {
  242. if (chartInstance) {
  243. chartInstance.scrollEnd(e);
  244. chartInstance.showToolTip(e, {
  245. format: function(item, category) {
  246. return category + ' ' + item.name + ':' + item.data
  247. }
  248. });
  249. }
  250. },
  251. switchTab(index) {
  252. this.activeTab = index;
  253. const name = this.tabs[index]?.name;
  254. this.currentPest = name
  255. this.getPestNameDetail(name);
  256. this.setChartData();
  257. this.$nextTick(() => {
  258. this.drawChart();
  259. });
  260. }
  261. }
  262. };
  263. </script>
  264. <style lang="scss" scoped>
  265. .pest-echart {
  266. overflow: hidden;
  267. margin-top: 24rpx;
  268. }
  269. .tab-list {
  270. width: 100%;
  271. overflow-x: scroll;
  272. white-space: nowrap;
  273. overflow-y: hidden;
  274. box-sizing: border-box;
  275. -ms-overflow-style: none;
  276. scrollbar-width: none;
  277. }
  278. .tab-item {
  279. margin-right: 16rpx;
  280. font-size: 28rpx;
  281. font-family: 'Source Han Sans CN VF', sans-serif;
  282. font-weight: 500;
  283. color: #999999;
  284. display: inline-block;
  285. box-sizing: border-box;
  286. background: #ffffff;
  287. font-family: "Source Han Sans CN VF";
  288. font-size: 28rpx;
  289. padding: 10rpx 32rpx;
  290. border-radius: 8rpx;
  291. &:last-child {
  292. margin-right: 0;
  293. }
  294. &.active {
  295. color: #0BBC58;
  296. font-weight: 700;
  297. }
  298. &:first-child.active {
  299. color: #0BBC58;
  300. }
  301. }
  302. .pest-echart__content {
  303. background: #FFFFFF;
  304. border-radius: 16rpx;
  305. padding: 0 32rpx 32rpx;
  306. }
  307. .period-section {
  308. display: flex;
  309. justify-content: space-between;
  310. padding: 24rpx;
  311. background: #ffffff;
  312. border-radius: 12rpx;
  313. }
  314. .period-item {
  315. flex: 1;
  316. display: flex;
  317. flex-direction: column;
  318. align-items: center;
  319. }
  320. .period-label {
  321. font-size: 28rpx;
  322. color: #303133;
  323. font-family: 'Source Han Sans CN VF', sans-serif;
  324. font-weight: 700;
  325. margin-bottom: 8rpx;
  326. }
  327. .period-value {
  328. font-size: 24rpx;
  329. color: #999999;
  330. font-family: 'Source Han Sans CN VF', sans-serif;
  331. font-weight: 400;
  332. }
  333. .chart-container {
  334. position: relative;
  335. width: 100%;
  336. height: 400rpx;
  337. border-radius: 12rpx;
  338. overflow: hidden;
  339. }
  340. .charts {
  341. width: 100%;
  342. height: 100%;
  343. }
  344. </style>