pestEchart.vue 9.0 KB

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