pestEchart.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  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. try{
  139. const res = await this.$myRequest({
  140. url: '/api/pest_name_detail',
  141. method: 'POST',
  142. data: {
  143. name
  144. },
  145. });
  146. this.deviceInfo = res
  147. this.$emit('getInfo',res)
  148. }catch(err){
  149. this.$emit('getInfo',{})
  150. }
  151. },
  152. async setChartData(){
  153. if(!this.currentPest){
  154. return;
  155. }
  156. if(!this.d_id || !this.endDate){
  157. return;
  158. }
  159. const res = await this.$myRequest({
  160. url: '/api/api_gateway?method=forecast.cbd_analysis.pest_predict_time',
  161. method: 'POST',
  162. data: {
  163. model:'B',
  164. d_id: this.d_id,
  165. year: this.endDate.split('-')[0],
  166. pest: this.currentPest,
  167. },
  168. });
  169. this.periodData = {
  170. firstDate: res[0][0],
  171. peakDate: res[1][0],
  172. lastDate: res[2][0],
  173. }
  174. },
  175. initChart() {
  176. this.$nextTick(() => {
  177. setTimeout(() => {
  178. this.drawChart(0);
  179. }, 100);
  180. });
  181. },
  182. drawChart(index) {
  183. const dayData = this.day || [];
  184. this.dayData = dayData;
  185. const pestData = this.pest[index];
  186. const ctx = uni.createCanvasContext('pestChart', this);
  187. chartInstance = new uCharts({
  188. context: ctx,
  189. type: 'line',
  190. fontSize: 11,
  191. legend: {
  192. show: false
  193. },
  194. background: '#FFFFFF',
  195. pixelRatio: this.pixelRatio,
  196. animation: true,
  197. dataLabel: false,
  198. categories: dayData,
  199. series: [{
  200. name: '',
  201. data: pestData
  202. }],
  203. color: ['#0085FF'],
  204. xAxis: {
  205. disableGrid: false,
  206. boundaryGap: 'justify',
  207. axisLine: true,
  208. lineColor: '#CCCCCC',
  209. fontColor: '#999999'
  210. },
  211. yAxis: {
  212. min: 0,
  213. splitNumber: 4,
  214. axisLine: true,
  215. lineColor: '#CCCCCC',
  216. fontColor: '#999999',
  217. gridType: 'dash',
  218. gridColor: '#E5E5E5'
  219. },
  220. width: this.cWidth * this.pixelRatio,
  221. height: this.cHeight * this.pixelRatio,
  222. extra: {
  223. line: {
  224. type: 'curve',
  225. width: 2,
  226. activeType: 'hollow'
  227. },
  228. tooltip: {
  229. showBox: true,
  230. bgOpacity: 0.7
  231. }
  232. }
  233. });
  234. },
  235. touchChart(e) {
  236. if (chartInstance) {
  237. chartInstance.scrollStart(e);
  238. }
  239. },
  240. moveChart(e) {
  241. if (chartInstance) {
  242. chartInstance.scroll(e);
  243. }
  244. },
  245. touchEndChart(e) {
  246. if (chartInstance) {
  247. chartInstance.scrollEnd(e);
  248. chartInstance.showToolTip(e, {
  249. format: function(item, category) {
  250. return category + ' ' + item.name + ':' + item.data
  251. }
  252. });
  253. }
  254. },
  255. switchTab(index) {
  256. this.activeTab = index;
  257. const name = this.tabs[index]?.name;
  258. this.currentPest = name
  259. this.getPestNameDetail(name);
  260. this.setChartData();
  261. this.$nextTick(() => {
  262. this.drawChart(index);
  263. });
  264. }
  265. }
  266. };
  267. </script>
  268. <style lang="scss" scoped>
  269. .pest-echart {
  270. overflow: hidden;
  271. margin-top: 24rpx;
  272. &__header{
  273. margin-bottom: 24rpx;
  274. }
  275. }
  276. .tab-list {
  277. width: 100%;
  278. overflow-x: scroll;
  279. white-space: nowrap;
  280. overflow-y: hidden;
  281. box-sizing: border-box;
  282. -ms-overflow-style: none;
  283. scrollbar-width: none;
  284. }
  285. .tab-item {
  286. margin-right: 16rpx;
  287. font-size: 28rpx;
  288. font-family: 'Source Han Sans CN VF', sans-serif;
  289. font-weight: 500;
  290. color: #999999;
  291. display: inline-block;
  292. box-sizing: border-box;
  293. background: #ffffff;
  294. font-family: "Source Han Sans CN VF";
  295. font-size: 28rpx;
  296. padding: 10rpx 32rpx;
  297. border-radius: 8rpx;
  298. &:last-child {
  299. margin-right: 0;
  300. }
  301. &.active {
  302. color: #0BBC58;
  303. font-weight: 700;
  304. }
  305. &:first-child.active {
  306. color: #0BBC58;
  307. }
  308. }
  309. .pest-echart__content {
  310. background: #FFFFFF;
  311. border-radius: 16rpx;
  312. padding: 0 32rpx 32rpx;
  313. }
  314. .period-section {
  315. display: flex;
  316. justify-content: space-between;
  317. padding: 24rpx;
  318. background: #ffffff;
  319. border-radius: 12rpx;
  320. }
  321. .period-item {
  322. flex: 1;
  323. display: flex;
  324. flex-direction: column;
  325. align-items: center;
  326. }
  327. .period-label {
  328. font-size: 28rpx;
  329. color: #303133;
  330. font-family: 'Source Han Sans CN VF', sans-serif;
  331. font-weight: 700;
  332. margin-bottom: 8rpx;
  333. }
  334. .period-value {
  335. font-size: 24rpx;
  336. color: #999999;
  337. font-family: 'Source Han Sans CN VF', sans-serif;
  338. font-weight: 400;
  339. }
  340. .chart-container {
  341. position: relative;
  342. width: 100%;
  343. height: 400rpx;
  344. border-radius: 12rpx;
  345. overflow: hidden;
  346. }
  347. .charts {
  348. width: 100%;
  349. height: 100%;
  350. }
  351. </style>