market-forecast.component.ts 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. import { Component, AfterViewInit, ViewChild, ElementRef, OnDestroy, ChangeDetectorRef } from '@angular/core';
  2. import * as echarts from 'echarts';
  3. import { CsvReaderService } from './csv-reader-service';
  4. import { IonicModule,ModalController } from '@ionic/angular';
  5. import { CommonModule } from '@angular/common';
  6. import { FormsModule, ReactiveFormsModule } from '@angular/forms'; // 导入 FormsModule 和 ReactiveFormsModule
  7. @Component({
  8. selector: 'app-market-forecast',
  9. templateUrl: './market-forecast.component.html',
  10. styleUrls: ['./market-forecast.component.scss'],
  11. standalone: true,
  12. imports: [IonicModule, CommonModule,FormsModule, ReactiveFormsModule]
  13. })
  14. export class MarketForecastComponent implements AfterViewInit, OnDestroy {
  15. @ViewChild('chartContainer', { static: false }) priceChartContainer!: ElementRef;
  16. @ViewChild('indexChartContainer', { static: false }) indexChartContainer!: ElementRef;
  17. isModalOpen = false;
  18. userEntries: { price?: string; index?: string }[] = [];
  19. private currentChart?: echarts.ECharts;
  20. public currentChartType: 'price' | 'index' = 'price'; // 明确限定类型
  21. constructor(private csvReaderService: CsvReaderService, private cdr: ChangeDetectorRef,private modalCtrl: ModalController) { }
  22. openDataEntryModal() {
  23. this.isModalOpen = true;
  24. this.userEntries.push({}); // 初始化至少一条记录
  25. }
  26. closeModal() {
  27. this.isModalOpen = false;
  28. }
  29. addEntry() {
  30. this.userEntries.push({});
  31. }
  32. onSubmit(form: any) {
  33. if (form.valid) {
  34. const jsonData = {
  35. "价格": this.userEntries.map(entry => entry.price).filter(Boolean),
  36. "指数": this.userEntries.map(entry => entry.index).filter(Boolean)
  37. };
  38. console.log(JSON.stringify(jsonData, null, 2));
  39. // 在这里你可以将 jsonData 发送到服务器或进行其他操作
  40. this.closeModal();
  41. }
  42. }
  43. ngAfterViewInit(): void {
  44. this.cdr.detectChanges(); // 确保变更检测运行以更新视图
  45. requestAnimationFrame(() => {
  46. this.selectChart(this.currentChartType);
  47. });
  48. }
  49. loadPredictChartData(): void {
  50. this.csvReaderService.getPredictData().subscribe(data => {
  51. this.updatePredictChart(data);
  52. });
  53. }
  54. updatePredictChart(data: any[]): void {
  55. if (!this.currentChart || !data) return;
  56. const dates = data.map(d => d['日期'].getTime());
  57. const prices = data.map(d => d['数值']);
  58. const indices = data.map(d => d['指数']);
  59. const xAxisConfig = {
  60. type: 'value',
  61. boundaryGap: false,
  62. name: '价格',
  63. min: Math.min(...prices), // 动态设置最小值
  64. max: Math.max(...prices), // 动态设置最大值
  65. axisLabel: {
  66. formatter: '{value}',
  67. rotate: -90 // 将 X 轴标签旋转 -90 度
  68. },
  69. axisLine: { show: true },
  70. axisTick: { show: true },
  71. axisPointer: {
  72. label: {
  73. formatter: function (params: any) {
  74. return params.value;
  75. }
  76. }
  77. }
  78. };
  79. const option = {
  80. title: { text: '预测脐橙价格', left: 'center' },
  81. tooltip: { trigger: 'axis' },
  82. xAxis: xAxisConfig,
  83. yAxis: {
  84. type: 'time',
  85. boundaryGap: false,
  86. name: '',
  87. axisLabel: {
  88. formatter: function (value: any) {
  89. const date = new Date(value);
  90. return `${date.getMonth() + 1}-${date.getDate()}`;
  91. },
  92. rotate: -90 // 将 Y 轴标签旋转 -90 度
  93. },
  94. axisLine: { show: true },
  95. axisTick: { show: true },
  96. axisPointer: {
  97. label: {
  98. formatter: function (params: any) {
  99. const date = new Date(params.value);
  100. return `${date.getMonth() + 1}-${date.getDate()}`;
  101. }
  102. }
  103. }
  104. },
  105. series: [{
  106. name: '预测价格',
  107. type: 'line',
  108. data: data.map((item, index) => [prices[index], dates[index]]),
  109. itemStyle: {
  110. color: 'red'
  111. }
  112. }],
  113. dataZoom: [
  114. {
  115. type: 'slider',
  116. show: true,
  117. yAxisIndex: [0],
  118. start: 50,
  119. end: 100
  120. },
  121. {
  122. type: 'inside',
  123. yAxisIndex: [0],
  124. start: 50,
  125. end: 100
  126. }
  127. ],
  128. graphic: [
  129. {
  130. type: 'text',
  131. left: 'middle',
  132. top: 'bottom',
  133. style: {
  134. text: '预测价格',
  135. textAlign: 'center',
  136. fontSize: 14,
  137. rotation: -Math.PI / 2, // 旋转 -90 度
  138. transformOrigin: 'center'
  139. }
  140. }
  141. ]
  142. };
  143. this.currentChart.setOption(option);
  144. }
  145. predictChart(){
  146. this.cdr.detectChanges(); // 触发变更检测以更新视图
  147. this.csvReaderService.getPredictData().subscribe(data => {
  148. this.clearChart();
  149. this.initChart();
  150. this.updatePredictChart(data); // 更新预测图表
  151. });
  152. }
  153. selectChart(type: 'price' | 'index'): void {
  154. this.currentChartType = type;
  155. this.cdr.detectChanges(); // 触发变更检测以更新视图
  156. // 延迟初始化以确保视图更新完毕
  157. setTimeout(() => {
  158. requestAnimationFrame(() => {
  159. this.clearChart();
  160. this.initChart();
  161. this.loadChartData();
  162. });
  163. }, 0);
  164. }
  165. initChart() {
  166. let chartContainerEl: HTMLElement | null = null;
  167. if (this.currentChartType === 'price') {
  168. chartContainerEl = this.priceChartContainer?.nativeElement;
  169. } else if (this.currentChartType === 'index') {
  170. chartContainerEl = this.indexChartContainer?.nativeElement;
  171. }
  172. // 检查图表容器是否已准备好,并且有非零尺寸
  173. if (chartContainerEl && chartContainerEl.offsetWidth > 0 && chartContainerEl.offsetHeight > 0) {
  174. this.currentChart = echarts.init(chartContainerEl);
  175. } else {
  176. console.warn('Chart container is not ready or has zero size.');
  177. // 如果图表容器尚未准备好,设置一个短暂的延时并再次尝试
  178. setTimeout(() => this.initChart(), 100);
  179. }
  180. }
  181. loadChartData(): void {
  182. this.csvReaderService.getOrangeData().subscribe(data => {
  183. this.updateChart(data);
  184. });
  185. }
  186. updateChart(data: any[]): void {
  187. if (!this.currentChart) return;
  188. const dates = data.map(d => d['日期'].getTime());
  189. const prices = data.map(d => d['数值']);
  190. const indices = data.map(d => d['指数']);
  191. const xAxisConfig = {
  192. type: 'value',
  193. boundaryGap: false,
  194. name: '',
  195. min: this.currentChartType === 'price' ? 6 : 90,
  196. max: this.currentChartType === 'price' ? 15 : 120,
  197. axisLabel: {
  198. formatter: function (value: any) {
  199. return value.toString();
  200. },
  201. rotate: -90 // 将 X 轴标签旋转 -90 度
  202. },
  203. axisLine: {
  204. show: true
  205. },
  206. axisTick: {
  207. show: true
  208. },
  209. axisPointer: {
  210. label: {
  211. formatter: function (params: any) {
  212. return params.value;
  213. }
  214. }
  215. }
  216. };
  217. const yAxisConfig = {
  218. type: 'time',
  219. boundaryGap: false,
  220. name: '',
  221. axisLabel: {
  222. formatter: function (value: any) {
  223. const date = new Date(value);
  224. return `${date.getMonth() + 1}-${date.getDate()}`;
  225. },
  226. rotate: -90 // 将 Y 轴标签旋转 -90 度
  227. },
  228. axisLine: {
  229. show: true
  230. },
  231. axisTick: {
  232. show: true
  233. },
  234. axisPointer: {
  235. label: {
  236. formatter: function (params: any) {
  237. const date = new Date(params.value);
  238. return `${date.getMonth() + 1}-${date.getDate()}`;
  239. }
  240. }
  241. }
  242. };
  243. const dataZoomConfig = [
  244. {
  245. type: 'slider',
  246. show: true,
  247. yAxisIndex: [0],
  248. start: 50,
  249. end: 100
  250. },
  251. {
  252. type: 'inside',
  253. yAxisIndex: [0],
  254. start: 50,
  255. end: 100
  256. }
  257. ];
  258. let option;
  259. if (this.currentChartType === 'price') {
  260. option = {
  261. title: { text: '每日脐橙价格', left: 'center' },
  262. tooltip: { trigger: 'axis' },
  263. xAxis: xAxisConfig,
  264. yAxis: yAxisConfig,
  265. series: [{
  266. name: '价格',
  267. type: 'line',
  268. data: data.map((item, index) => [prices[index], dates[index]]),
  269. itemStyle: {
  270. color: 'orange'
  271. }
  272. }],
  273. dataZoom: dataZoomConfig,
  274. graphic: [
  275. {
  276. type: 'text',
  277. left: 'middle',
  278. top: 'bottom',
  279. style: {
  280. text: '价格',
  281. textAlign: 'center',
  282. fontSize: 14,
  283. rotation: -Math.PI / 2, // 旋转 -90 度
  284. transformOrigin: 'center'
  285. }
  286. }
  287. ]
  288. };
  289. } else {
  290. option = {
  291. title: { text: '脐橙价格指数', left: 'center' },
  292. tooltip: { trigger: 'axis' },
  293. xAxis: xAxisConfig,
  294. yAxis: yAxisConfig,
  295. series: [{
  296. name: '指数',
  297. type: 'line',
  298. data: data.map((item, index) => [indices[index], dates[index]])
  299. }],
  300. dataZoom: dataZoomConfig,
  301. graphic: [
  302. {
  303. type: 'text',
  304. left: 'middle',
  305. top: 'bottom',
  306. style: {
  307. text: '指数',
  308. textAlign: 'center',
  309. fontSize: 14,
  310. rotation: -Math.PI / 2, // 旋转 -90 度
  311. transformOrigin: 'center'
  312. }
  313. }
  314. ]
  315. };
  316. }
  317. this.currentChart.setOption(option);
  318. }
  319. clearChart(): void {
  320. if (this.currentChart) {
  321. this.currentChart.dispose();
  322. this.currentChart = undefined;
  323. }
  324. }
  325. ngOnDestroy(): void {
  326. this.clearChart();
  327. }
  328. }