@tsingroc/tsingroc-components 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1120 @@
1
+ import React, { useEffect, useMemo, useRef,useState } from "react";
2
+ import DayJs from "dayjs";
3
+ import ReactECharts from "echarts-for-react";
4
+ import * as echarts from "echarts";
5
+ import { LeftOutlined, RightOutlined } from "@ant-design/icons"
6
+ import { DatePicker } from "antd"
7
+
8
+ // 带风险区间的折线图
9
+ const RiskEchartMult = React.memo(function EchartMult(props) {
10
+ const l = localStorage.getItem("lang");
11
+ const {
12
+ riskArr,
13
+ max,
14
+ x: propsX,
15
+ yArr,
16
+ nameArr,
17
+ notFormat,
18
+ nofoot,
19
+ step,
20
+ h = "350px",
21
+ w = "100%",
22
+ noDiff,
23
+ addMaxBar,
24
+ echartColorArr = [
25
+ "#2FDAFF",
26
+ "#77DA9B",
27
+ "#FFB82E",
28
+ "#5470c6",
29
+ "#91cc75",
30
+ "#fac858",
31
+ "#ee6666",
32
+ "#73c0de",
33
+ "#3ba272",
34
+ "#fc8452",
35
+ "#9a60b4",
36
+ "#ea7ccc",
37
+ ],
38
+ areaStyle = ['rgba(47,218,255 ,0.3)', 'rgba(119,218,155,0.3)', 'rgba(255,184,46,0.3)'],
39
+ yAxis = "功率(MW)",
40
+ barColor,
41
+ legendLeft,
42
+ unit = "MW",
43
+ specialLine,
44
+ pickLine,
45
+ type
46
+ } = props;
47
+ const xTemp = useMemo(() => {
48
+ return new Array(24).fill(0).map((item, index) => {
49
+ let time = (index + 1).toString().padStart(2, "0");
50
+ return time + ":00";
51
+ });
52
+ }, []);
53
+ let x = [];
54
+ if (!propsX) {
55
+ x = xTemp;
56
+ } else {
57
+ x = propsX;
58
+ }
59
+ // 获取最大值,构建bar
60
+ const option = useMemo(() => {
61
+ let barArr = [];
62
+ if (Array.isArray(yArr) && yArr.length > 0 && addMaxBar) {
63
+ let length = yArr[0].length;
64
+ for (let i = 0; i < length; i++) {
65
+ let max = -Infinity;
66
+ let tempIndex = -1;
67
+ let secendLoopLength = yArr.length;
68
+ for (let j = 0; j < secendLoopLength; j++) {
69
+ if (yArr[j][i] > max) {
70
+ max = yArr[j][i];
71
+ tempIndex = j;
72
+ }
73
+ }
74
+ barArr.push({
75
+ value: max,
76
+ itemStyle: {
77
+ color: barColor[tempIndex],
78
+ },
79
+ });
80
+ }
81
+ }
82
+
83
+ const option = {
84
+ grid: {
85
+ top: "30",
86
+ left: "40",
87
+ right: "40",
88
+ bottom: "20",
89
+ containLabel: true,
90
+ },
91
+ xAxis: {
92
+ type: "category",
93
+ show: !nofoot,
94
+ data: notFormat ? x.map((item) => new DayJs(item).format("HH:mm")) : x,
95
+ },
96
+ yAxis: [
97
+ {
98
+ max: max,
99
+ name: yAxis,
100
+ nameGap: 10,
101
+ position: "left",
102
+ nameTextStyle: {
103
+ align: "center",
104
+ fontWeight: "bold",
105
+ },
106
+ type: "value",
107
+ splitLine: {
108
+ lineStyle: {
109
+ color: "rgba(130, 144, 157, 0.18)",
110
+ },
111
+ },
112
+ splitNumber: 3,
113
+ },
114
+ {
115
+ type: "value",
116
+ splitLine: {
117
+ lineStyle: {
118
+ color: "#DCDEE0",
119
+ },
120
+ },
121
+ splitNumber: 3,
122
+ },
123
+ ],
124
+ legend: {
125
+ data: [...nameArr, '风险区间'].map((item, index) => {
126
+ return {
127
+ name: item,
128
+ itemStyle: {
129
+ color: item === '风险区间' ? 'rgba(99,211,149,0.8)' : echartColorArr[index],
130
+ },
131
+ icon:
132
+ item === "实际"
133
+ ? "path://M304.43 532.76H221.4c-11.47 0-20.76-9.3-20.76-20.76s9.29-20.76 20.76-20.76h83.03c11.47 0 20.76 9.3 20.76 20.76s-9.29 20.76-20.76 20.76zM581.19 532.76H442.81c-11.47 0-20.76-9.3-20.76-20.76s9.29-20.76 20.76-20.76h138.38c11.47 0 20.76 9.3 20.76 20.76s-9.3 20.76-20.76 20.76zM802.59 532.76h-83.03c-11.47 0-20.76-9.3-20.76-20.76s9.29-20.76 20.76-20.76h83.03c11.47 0 20.76 9.3 20.76 20.76s-9.29 20.76-20.76 20.76z"
134
+ : "rect",
135
+ };
136
+ }),
137
+ textStyle: {
138
+ color: "#646566",
139
+ },
140
+ left: legendLeft,
141
+ right: "20",
142
+ // formatter: (name) => {
143
+ // return shark(name,l)
144
+ // }
145
+ },
146
+ color: echartColorArr,
147
+ tooltip: {
148
+ trigger: "axis",
149
+ valueFormatter: (value) =>
150
+ value ? value.toFixed(2) + unit : value === 0 ? 0 + unit : "",
151
+ },
152
+ series: yArr
153
+ ?.map((item, index) => ({
154
+ data: item?.map((i) => (typeof i === "number" ? i : undefined)),
155
+ type: type || "line",
156
+ smooth: true,
157
+ name: nameArr[index],
158
+ lineStyle: {
159
+ // type: noDiff ? "solid" : index === 0 ? "dashed" : "solid",
160
+ type: specialLine ? nameArr[index] === pickLine ? 'solid' : "dashed" : "solid",
161
+ color: echartColorArr[index],
162
+ },
163
+ step: step || "",
164
+ symbol: "none",
165
+ emphasis: {
166
+ itemStyle: {
167
+ color: "red",
168
+ },
169
+ },
170
+ // areaStyle:{
171
+ // color:areaStyle[index]
172
+ // }
173
+ }))
174
+ .concat(
175
+ addMaxBar
176
+ ? [
177
+ {
178
+ data: barArr,
179
+ type: "bar",
180
+ name: "max",
181
+ barWidth: "101%",
182
+ yAxisIndex: 1,
183
+ tooltip: {
184
+ show: false,
185
+ },
186
+ },
187
+ ]
188
+ : []
189
+ ),
190
+ };
191
+ if (riskArr && riskArr.length > 0 && riskArr[0]) {
192
+ option.series.push({
193
+ data: riskArr[1],
194
+ type: 'line',
195
+ name: "风险上边界",
196
+ smooth: true,
197
+ showSymbol: false,
198
+ tooltip: {
199
+ show: true
200
+ },
201
+ lineStyle: {
202
+ opacity: 0
203
+ },
204
+ }, {
205
+ yAxisIndex: 0,
206
+ data: riskArr[0],
207
+ type: 'line',
208
+ name: "风险下边界",
209
+ smooth: true,
210
+ stack: 'st',
211
+ showSymbol: false,
212
+ lineStyle: {
213
+ opacity: 0
214
+ },
215
+ tooltip: {
216
+ show: true
217
+ },
218
+ emphasis: {
219
+ disabled: true
220
+ },
221
+ }, {
222
+ yAxisIndex: 0,
223
+ name: '风险区间',
224
+ stack: 'st',
225
+ stackStrategy: "all",
226
+ data: riskArr[1].map((item, index) => {
227
+ return riskArr[1][index] - riskArr[0][index]
228
+ }),
229
+ type: 'line',
230
+ smooth: true,
231
+ symbol: "none",
232
+ lineStyle: {
233
+ opacity: 0
234
+ },
235
+ areaStyle: {
236
+ color: "#63D395",
237
+ },
238
+ emphasis: {
239
+ disabled: true
240
+ },
241
+ tooltip: {
242
+ show: false
243
+ },
244
+ },)
245
+ }
246
+
247
+ return option;
248
+ }, [
249
+ echartColorArr,
250
+ addMaxBar,
251
+ x,
252
+ yArr,
253
+ nameArr,
254
+ noDiff,
255
+ nofoot,
256
+ step,
257
+ yAxis,
258
+ notFormat,
259
+ barColor,
260
+ riskArr
261
+ ]);
262
+ const echartRef = useRef(null);
263
+ useEffect(() => {
264
+ if (echartRef.current) {
265
+ const ins = echartRef.current.getEchartsInstance();
266
+ }
267
+ }, [x, yArr, nameArr, riskArr, option]);
268
+
269
+ const onEvents = {
270
+ " click": () => { },
271
+ };
272
+ return (
273
+ <ReactECharts
274
+ ref={echartRef}
275
+ option={option}
276
+ style={{ height: h, width: w }}
277
+ onEvents={onEvents}
278
+ />
279
+ );
280
+ });
281
+
282
+ // 折线图
283
+ const EchartMult = React.memo(function EchartMult(props) {
284
+ const l = localStorage.getItem("lang");
285
+ const {
286
+ max,
287
+ x: propsX,
288
+ yArr,
289
+ nameArr,
290
+ notFormat,
291
+ nofoot,
292
+ step,
293
+ h = "350px",
294
+ w = "100%",
295
+ left=30,
296
+ right=40,
297
+ noDiff,
298
+ addMaxBar,
299
+ barWidth = '15px',
300
+ barLinearGradientArr,
301
+ echartColorArr = [
302
+ "#2FDAFF",
303
+ "#77DA9B",
304
+ "#FFB82E",
305
+ "#5470c6",
306
+ "#91cc75",
307
+ "#fac858",
308
+ "#ee6666",
309
+ "#73c0de",
310
+ "#3ba272",
311
+ "#fc8452",
312
+ "#9a60b4",
313
+ "#ea7ccc",
314
+ ],
315
+ areaStyle = ['rgba(47,218,255 ,0.3)', 'rgba(119,218,155,0.3)', 'rgba(255,184,46,0.3)'],
316
+ yAxis = "功率(MW)",
317
+ barColor,
318
+ legendLeft,
319
+ unit = "MW",
320
+ specialLine,
321
+ pickLine,
322
+ type
323
+ } = props;
324
+ if (barLinearGradientArr) {
325
+ echartColorArr[0] = barLinearGradientArr[1]
326
+ }
327
+ const xTemp = useMemo(() => {
328
+ return new Array(24).fill(0).map((item, index) => {
329
+ let time = (index + 1).toString().padStart(2, "0");
330
+ return time + ":00";
331
+ });
332
+ }, []);
333
+ let x = [];
334
+ if (!propsX) {
335
+ x = xTemp;
336
+ } else {
337
+ x = propsX;
338
+ }
339
+ // 获取最大值,构建bar
340
+ const option = useMemo(() => {
341
+ let barArr = [];
342
+ if (Array.isArray(yArr) && yArr.length > 0 && addMaxBar) {
343
+ let length = yArr[0].length;
344
+ for (let i = 0; i < length; i++) {
345
+ let max = -Infinity;
346
+ let tempIndex = -1;
347
+ let secendLoopLength = yArr.length;
348
+ for (let j = 0; j < secendLoopLength; j++) {
349
+ if (yArr[j][i] > max) {
350
+ max = yArr[j][i];
351
+ tempIndex = j;
352
+ }
353
+ }
354
+ barArr.push({
355
+ value: max,
356
+ itemStyle: {
357
+ color: barColor[tempIndex],
358
+ },
359
+ });
360
+ }
361
+ }
362
+
363
+ const option = {
364
+ grid: {
365
+ top: "30",
366
+ left: left,
367
+ right: right,
368
+ bottom: "20",
369
+ containLabel: true,
370
+ },
371
+ xAxis: {
372
+ type: "category",
373
+ // axisLabel: {
374
+ // interval: 0 // 强制显示所有x轴标签
375
+ // },
376
+ show: !nofoot,
377
+ data: notFormat ? x.map((item) => new DayJs(item).format("HH:mm")) : x,
378
+ },
379
+ yAxis: [
380
+ {
381
+ max: max,
382
+ name: yAxis,
383
+ nameGap: 10,
384
+ position: "left",
385
+ nameTextStyle: {
386
+ align: "center",
387
+ fontWeight: "bold",
388
+ },
389
+ type: "value",
390
+ splitLine: {
391
+ lineStyle: {
392
+ color: "rgba(130, 144, 157, 0.18)",
393
+ },
394
+ },
395
+ splitNumber: 3,
396
+ },
397
+ {
398
+ type: "value",
399
+ splitLine: {
400
+ lineStyle: {
401
+ color: "#DCDEE0",
402
+ },
403
+ },
404
+ splitNumber: 3,
405
+ },
406
+ ],
407
+ legend: {
408
+ data: nameArr.map((item, index) => {
409
+ return {
410
+ name: item,
411
+ itemStyle: {
412
+ color: echartColorArr[index],
413
+ },
414
+ icon:
415
+ item === "实际"
416
+ ? "path://M304.43 532.76H221.4c-11.47 0-20.76-9.3-20.76-20.76s9.29-20.76 20.76-20.76h83.03c11.47 0 20.76 9.3 20.76 20.76s-9.29 20.76-20.76 20.76zM581.19 532.76H442.81c-11.47 0-20.76-9.3-20.76-20.76s9.29-20.76 20.76-20.76h138.38c11.47 0 20.76 9.3 20.76 20.76s-9.3 20.76-20.76 20.76zM802.59 532.76h-83.03c-11.47 0-20.76-9.3-20.76-20.76s9.29-20.76 20.76-20.76h83.03c11.47 0 20.76 9.3 20.76 20.76s-9.29 20.76-20.76 20.76z"
417
+ : "rect",
418
+ };
419
+ }),
420
+ textStyle: {
421
+ // color: "#646566",
422
+ color: '#fff'
423
+ },
424
+ left: legendLeft,
425
+ right: "20",
426
+ // formatter: (name) => {
427
+ // return shark(name,l)
428
+ // }
429
+ },
430
+ color: echartColorArr,
431
+ tooltip: {
432
+ trigger: "axis",
433
+ valueFormatter: (value) =>
434
+ value ? value.toFixed(2) + unit : value === 0 ? 0 + unit : "",
435
+ },
436
+ series: yArr
437
+ ?.map((item, index) => ({
438
+ data: item?.map((i) => (typeof i === "number" ? i : undefined)),
439
+ type: type || "line",
440
+ smooth: true,
441
+ name: nameArr[index],
442
+ lineStyle: {
443
+ // type: noDiff ? "solid" : index === 0 ? "dashed" : "solid",
444
+ type: specialLine ? nameArr[index] === pickLine ? 'solid' : "dashed" : "solid",
445
+ color: echartColorArr[index],
446
+ },
447
+ step: step || "",
448
+ symbol: "none",
449
+ emphasis: {
450
+ itemStyle: {
451
+ color: "red",
452
+ },
453
+ },
454
+ areaStyle:areaStyle? {
455
+ color: areaStyle[index]
456
+ }:null,
457
+ barWidth: barWidth,
458
+ itemStyle: barLinearGradientArr ? {
459
+ barBorderRadius: [3, 3, 0, 0],
460
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
461
+ { offset: 0, color: barLinearGradientArr[1] },
462
+ { offset: 1, color: barLinearGradientArr[0] }
463
+ ])
464
+ } : {}
465
+ }))
466
+ .concat(
467
+ addMaxBar
468
+ ? [
469
+ {
470
+ data: barArr,
471
+ type: "bar",
472
+ name: "max",
473
+ barWidth: "101%",
474
+ yAxisIndex: 1,
475
+ tooltip: {
476
+ show: false,
477
+ },
478
+ },
479
+ ]
480
+ : []
481
+ ),
482
+ };
483
+ return option;
484
+ }, [
485
+ echartColorArr,
486
+ addMaxBar,
487
+ x,
488
+ yArr,
489
+ nameArr,
490
+ noDiff,
491
+ nofoot,
492
+ step,
493
+ yAxis,
494
+ notFormat,
495
+ barColor,
496
+ ]);
497
+ const echartRef = useRef(null);
498
+ useEffect(() => {
499
+ if (echartRef.current) {
500
+ const ins = echartRef.current.getEchartsInstance();
501
+ ins.setOption(
502
+ {
503
+ ...ins.getOption(),
504
+ series: ins
505
+ .getOption()
506
+ .series.filter((item) => [...nameArr, "max"].includes(item.name)),
507
+ },
508
+ true,
509
+ true
510
+ );
511
+ }
512
+ }, [x, yArr, nameArr]);
513
+
514
+ const onEvents = {
515
+ " click": () => { },
516
+ };
517
+ return (
518
+ <ReactECharts
519
+ ref={echartRef}
520
+ option={option}
521
+ style={{ height: h, width: w }}
522
+ onEvents={onEvents}
523
+ />
524
+ );
525
+ });
526
+
527
+ // 待快速跳转的日,周,月 选择器
528
+ function TsingrocDatePick(props){
529
+ const {value,onChange:propsOnChange,dataType ='month',className,popupClassName}=props
530
+ const [dateValue,setDateValue]=useState(value)
531
+ const quickGo=(type)=>{
532
+ let temp = dateValue.add(type, dataType)
533
+ setDateValue(temp)
534
+ propsOnChange && propsOnChange(temp)
535
+ }
536
+ const onChange=(value)=>{
537
+ setDateValue(value)
538
+ propsOnChange && propsOnChange(value)
539
+ }
540
+ return <div>
541
+ <div onClick={() => { quickGo(-1) }} className={`quick-jump ${className}`} style={{borderRight: 'none', borderRadius: '2px 0 0 2px' }}>
542
+ <LeftOutlined className="quick-icon" />
543
+ </div>
544
+ {
545
+ dataType === 'day' && <DatePicker value={dateValue} onChange={onChange} />
546
+ }
547
+ {
548
+ dataType === 'week' && <DatePicker value={dateValue} onChange={onChange} picker="week" />
549
+ }
550
+ {
551
+ dataType === 'month' && <DatePicker popupClassName={popupClassName} className={className} value={dateValue} onChange={onChange} picker="month" />
552
+ }
553
+ <div onClick={() => { quickGo(1) }} className={`quick-jump ${className}`} style={{ borderLeft:'none', borderRadius: '0 2px 2px 0' }}>
554
+ <RightOutlined className="quick-icon" />
555
+ </div>
556
+
557
+ </div>
558
+ }
559
+ // 雷达图
560
+ function RadarEchart(props) {
561
+ const { h = '100px', x = [], yAxis, arr = [], value = [] } = props
562
+ const echartRef = useRef(null);
563
+ const option = {
564
+ // grid: {
565
+ // top: "20%",
566
+ // left: "40",
567
+ // right: "40",
568
+ // containLabel: true,
569
+ // },
570
+ radar: {
571
+ indicator: arr.map((item, key) => {
572
+ return { name: item, max: 1 }
573
+ }),
574
+ // indicator: [
575
+ // { name: 'Sales', max: 6500 },
576
+ // { name: 'Administration', max: 16000 },
577
+ // { name: 'Information Technology', max: 30000 },
578
+ // { name: 'Customer Support', max: 38000 },
579
+ // ],
580
+ radius: '60%',
581
+ splitArea: {
582
+ areaStyle: {
583
+ color: 'rgba(119, 218, 155, 0)' // 设置分隔区域颜色为渐变色
584
+ }
585
+ },
586
+ splitLine: {
587
+ lineStyle: {
588
+ color: 'rgba(64, 175, 239, 0.15)'
589
+ }
590
+ },
591
+ axisLine: {
592
+ // show: false,
593
+ lineStyle: {
594
+ with: 1,
595
+ color: "rgba(130, 144, 157, 0.18)",
596
+ },
597
+ },
598
+ axisName: {
599
+ show: true,
600
+ color: "#9FC7DD",
601
+ }
602
+ },
603
+ series: [
604
+ {
605
+ name: 'Budget vs spending',
606
+ type: 'radar',
607
+ data: [
608
+ {
609
+ value: value,
610
+ name: 'Actual Spending'
611
+ }
612
+ ],
613
+ lineStyle: {
614
+ normal: {
615
+ color: '#77DA9B' // 设置线条颜色为橙色,透明度为0.5
616
+ }
617
+ },
618
+ areaStyle: {
619
+ color: 'rgba(119, 218, 155, 0.3)'
620
+ },
621
+ symbol: "none",
622
+ }
623
+
624
+ ]
625
+ };
626
+ const onEvents = () => { }
627
+ return <ReactECharts
628
+ ref={echartRef}
629
+ option={option}
630
+ style={{ height: h, width: "100%" }}
631
+ onEvents={onEvents}
632
+ />
633
+
634
+ }
635
+ // 饼图
636
+ function TsingrocPie(props) {
637
+ const {
638
+ name = "饼图",
639
+ valueArr = [
640
+ { value: 735, name: 'Search Engine' },
641
+ { value: 735, name: 'Direct' },
642
+ { value: 735, name: 'Email' },
643
+ { value: 735, name: 'Union Ads' },
644
+ { value: 735, name: 'Video Ads' }
645
+ ], color = ["#ECA926", "#32ADF4", "#DC7756", '#62CE89', '#4256B9', '#00D0FF', '#96D0DD']
646
+ } = props
647
+ const option = {
648
+ color: color,
649
+ tooltip: {
650
+ trigger: 'item'
651
+ },
652
+ legend: {
653
+ top: '5%',
654
+ left: 'center',
655
+ show: false
656
+ },
657
+ series: [
658
+ {
659
+ name: name,
660
+ type: 'pie',
661
+ radius: ['40%', '70%'],
662
+ avoidLabelOverlap: false,
663
+ padAngle: 2,
664
+ itemStyle: {
665
+ borderRadius: 1
666
+ },
667
+ label: {
668
+ show: false,
669
+ position: 'center'
670
+ },
671
+ emphasis: {
672
+ label: {
673
+ show: true,
674
+ fontSize: 16,
675
+ fontWeight: 'bold'
676
+ }
677
+ },
678
+ labelLine: {
679
+ show: false
680
+ },
681
+ data: valueArr
682
+ }
683
+ ]
684
+ };
685
+ return <div style={{ height: "100%" }}>
686
+ <div style={{ display: 'flex', height: "100%" }}>
687
+ <div style={{ flex: 3 }}>
688
+ <ReactECharts style={{ height: '100%' }} option={option} />
689
+ </div>
690
+ <div style={{ flex: 2, display: "flex", alignItems: 'center' }}>
691
+ <div style={{}}>
692
+ {
693
+ valueArr.map((item, key) => {
694
+ return <div key={key} style={{ fontSize: '14px', lineHeight: "20px" }}>
695
+ <div style={{ display: 'inline-block', width: '6px', height: '6px', backgroundColor: color[key], marginRight: "5px", transform: "translateY(-1px)" }}></div>
696
+ <Space>
697
+ <div style={{ color: "#96A9BA" }}>{item.name}:</div>
698
+ <div style={{ color: '#fff' }}>{item.value}</div>
699
+ </Space>
700
+ </div>
701
+ })
702
+ }
703
+ </div>
704
+ </div>
705
+ </div>
706
+ </div>
707
+
708
+ }
709
+ // 环形统计图
710
+ const CircularProgress = ({ progress,color="#34A9D4" }) => {
711
+ // 确保进度值在0到100之间
712
+ const clampProgress = (value) => Math.max(0, Math.min(100, value));
713
+
714
+ // 计算圆的周长
715
+ const circumference = 2 * Math.PI * 15.91549430918954;
716
+
717
+ return (
718
+ <div style={{ width: "100%", height: "100%", position: 'relative' }}>
719
+ <svg
720
+ width="100%"
721
+ height="100%"
722
+ viewBox="0 0 42 42"
723
+ xmlns="http://www.w3.org/2000/svg"
724
+ >
725
+ <circle
726
+ className="circle"
727
+ cx="21"
728
+ cy="21"
729
+ r="15.91549430918954"
730
+ stroke="#22405a"
731
+ strokeWidth="3"
732
+ fill="none"
733
+ />
734
+ <circle
735
+ className="circle"
736
+ cx="21"
737
+ cy="21"
738
+ r="15.91549430918954"
739
+ stroke={color}
740
+ // stroke="#ff0"
741
+ strokeWidth="3"
742
+ fill="none"
743
+ strokeDasharray={circumference}
744
+ strokeDashoffset={circumference - (clampProgress(progress) / 100) * circumference}
745
+ />
746
+ </svg>
747
+ <div style={{ position: "absolute", top: '50%', transform: 'translateY(-50%)', textAlign: "center", width: "100%" }}>{clampProgress(progress)}%</div>
748
+ </div>
749
+ );
750
+ };
751
+
752
+ // 风向模拟
753
+ const WindFlow=React.memo(function WindFlowFunc(props) {
754
+ const { weatherData } = props
755
+ const echartRef = useRef(null)
756
+ const { h = '150%' } = props
757
+ const noise = createNoise2D(Math.random);
758
+ const noise2 = createNoise2D(Math.random)
759
+ // returns a value between -1 and 1
760
+ let valMin = Infinity;
761
+ let valMax = -Infinity;
762
+ let tempMap = {}
763
+ let orderArr = []
764
+ weatherData.forEach((item) => {
765
+ let a = item.location.split(" ")
766
+ let x = a[0]
767
+ let y = a[1]
768
+ if (tempMap[x]) {
769
+ tempMap[x].push(item)
770
+ } else {
771
+ orderArr.push(x)
772
+ tempMap[x] = [item]
773
+ }
774
+ })
775
+ orderArr.sort((a, b) => {
776
+ return a - b
777
+ })
778
+ Object.keys(tempMap).forEach((key) => {
779
+ tempMap[key].sort((a, b) => {
780
+ return a.location.split(" ")[1] - b.location.split(" ")[1]
781
+ })
782
+ })
783
+ let retArr = []
784
+ orderArr.forEach((key, index) => {
785
+ retArr[index] = tempMap[key]
786
+ })
787
+ console.log(retArr)
788
+
789
+ function generateData() {
790
+ var data = [];
791
+ // for (var i = 0; i <= 100; i++) {
792
+ // for (var j = 0; j <= 100; j++) {
793
+ // var dx = noise(i / 150, j / 150);
794
+ // var dy = noise2(i / 150, j / 150);
795
+ // var mag = Math.sqrt(dx * dx + dy * dy);
796
+ // valMax = Math.max(valMax, mag);
797
+ // valMin = Math.min(valMin, mag);
798
+ // // data.push([i, j, dx, dy, mag]);
799
+ // data.push([i, j, 0.2 + 0.01 * i, -0.9 + i * 0.01, 1])
800
+ // }
801
+ // }
802
+
803
+ // todo
804
+ retArr.forEach((item, i) => {
805
+ item.forEach((sub, j) => {
806
+ data.push([i, j, -Math.cos((sub.wind_direction_80m) * (Math.PI / 180)), Math.sin((sub.wind_direction_80m) * (Math.PI / 180)), 1])
807
+ })
808
+ }
809
+ )
810
+ return data;
811
+ }
812
+ const data = generateData()
813
+ // const data = windData
814
+ const onEvents = {
815
+ " click": () => { },
816
+ };
817
+ const option = {
818
+ grid: {
819
+ left: 0,
820
+ top: 0,
821
+ bottom: 0,
822
+ right: 0
823
+ },
824
+ visualMap: {
825
+ show: false,
826
+ min: valMin,
827
+ max: valMax,
828
+ dimension: 2,
829
+ inRange: {
830
+ color: [
831
+ '#fff',
832
+ ]
833
+ }
834
+ },
835
+
836
+ xAxis: {
837
+ type: 'value',
838
+ axisLine: {
839
+
840
+ lineStyle: {
841
+ color: '#fff'
842
+ }
843
+ },
844
+ splitLine: {
845
+ show: false,
846
+ lineStyle: {
847
+ color: 'rgba(255,255,255,0.2)'
848
+ }
849
+ }
850
+ },
851
+ yAxis: {
852
+ type: 'value',
853
+ axisLine: {
854
+ lineStyle: {
855
+ color: '#fff'
856
+ }
857
+ },
858
+ show: false,
859
+ splitLine: {
860
+ show: false,
861
+ lineStyle: {
862
+ color: 'rgba(255,255,255,0.2)'
863
+ }
864
+ }
865
+ },
866
+ series: [
867
+ {
868
+ type: 'flowGL',
869
+ data: data,
870
+ particleDensity: 20,
871
+ particleSize: 3,
872
+ // particleDensity: 15,
873
+ // particleSize: 3,
874
+ particleSpeed: 0.5,
875
+ itemStyle: {
876
+ opacity: 0.5
877
+ }
878
+ },
879
+ // {
880
+ // type: 'custom',
881
+ // data: data,
882
+ // encode: {
883
+ // x: 0,
884
+ // y: 0
885
+ // },
886
+ // renderItem: function (params, api) {
887
+ // var x = api.value(0),
888
+ // y = api.value(1),
889
+ // dx = api.value(2),
890
+ // dy = api.value(3);
891
+ // var start = api.coord([x - dx / 2, y - dy / 2]);
892
+ // var end = api.coord([x + dx / 2, y + dy / 2]);
893
+ // return {
894
+ // type: 'line',
895
+ // shape: {
896
+ // x1: start[0],
897
+ // y1: start[1],
898
+ // x2: end[0],
899
+ // y2: end[1]
900
+ // },
901
+ // style: {
902
+ // lineWidth: 2,
903
+ // stroke: '#fff',
904
+ // opacity: 1
905
+ // }
906
+ // };
907
+ // }
908
+ // }
909
+ ]
910
+ }
911
+
912
+ return (
913
+ <ReactECharts
914
+ ref={echartRef}
915
+ option={option}
916
+ style={{ height: h, width: "110%" ,marginTop:"-100px"}}
917
+ onEvents={onEvents}
918
+ />
919
+ );
920
+ }
921
+ )
922
+ // 箱线图
923
+ function BoxplotEchart(props) {
924
+ const { h = '100px', x = [], yAxis, yArr = [] } = props
925
+ const echartRef = useRef(null);
926
+ const option = {
927
+ tooltip: {
928
+ trigger: 'item',
929
+ axisPointer: {
930
+ type: 'shadow'
931
+ }
932
+ },
933
+ grid: {
934
+ top: "30",
935
+ left: "40",
936
+ right: "40",
937
+ bottom: "20",
938
+ containLabel: true,
939
+ },
940
+ xAxis: {
941
+ type: 'category',
942
+ boundaryGap: true,
943
+ splitArea: {
944
+ show: false
945
+ },
946
+ splitLine: {
947
+ show: false
948
+ },
949
+ data: x,
950
+ },
951
+ yAxis: {
952
+ type: 'value',
953
+ max: 50,
954
+ min: -50,
955
+ name: yAxis || 'km/s minus 299,000',
956
+ splitLine: {
957
+ lineStyle: {
958
+ color: "rgba(130, 144, 157, 0.18)",
959
+ },
960
+ },
961
+ splitNumber: 3,
962
+ },
963
+ series: [
964
+ {
965
+ name: 'boxplot',
966
+ type: 'boxplot',
967
+ data: yArr,
968
+ tooltip: {
969
+ formatter: function (param) {
970
+ return [
971
+ '时间 ' + param.name,
972
+ '最大值: ' + param.data[5].toFixed(2) + '%',
973
+ '第三四分位数: ' + param.data[4].toFixed(2) + '%',
974
+ '中位数: ' + param.data[2].toFixed(2) + '%',
975
+ '第一四分位数: ' + param.data[3].toFixed(2) + '%',
976
+ '最小值: ' + param.data[1].toFixed(2) + '%'
977
+ ].join('<br/>')
978
+ }
979
+ }
980
+ }
981
+ ]
982
+ };
983
+ const onEvents = () => { }
984
+ return <ReactECharts
985
+ ref={echartRef}
986
+ option={option}
987
+ style={{ height: h, width: "100%" }}
988
+ onEvents={onEvents}
989
+ />
990
+ }
991
+
992
+
993
+ function EchartJustOneLine(props) {
994
+ const {
995
+ x=[], y=[], h = '350px', styleObj = {},bottom,left="0%",isLine,w,top='0%'
996
+ } = props;
997
+ const option = {
998
+ grid: {
999
+ left: left,
1000
+ right:'0',
1001
+ top: top,
1002
+ },
1003
+ xAxis: {
1004
+ type: 'category',
1005
+ data: x,
1006
+ show: isLine,
1007
+ axisLine: {
1008
+ show: isLine
1009
+ },
1010
+ },
1011
+ yAxis: {
1012
+ splitLine: {
1013
+ show: false
1014
+ },
1015
+ type: 'value'
1016
+ },
1017
+ series: [
1018
+ {
1019
+ data: y,
1020
+ type: 'line',
1021
+ smooth: true,
1022
+ name: "电价",
1023
+ symbol: 'none',
1024
+ lineStyle: {
1025
+ color: styleObj.lineColor
1026
+ },
1027
+ areaStyle: {
1028
+ color: {
1029
+ type: 'linear',
1030
+ x: 0,
1031
+ y: 0,
1032
+ x2: 0,
1033
+ y2: 1,
1034
+ colorStops: [{
1035
+ offset: 0, color: styleObj.areaStyle ? styleObj.areaStyle[0] : '', // 0% 处的颜色
1036
+ }, {
1037
+ offset: 1, color: styleObj.areaStyle ? styleObj.areaStyle[1] : '', // 100% 处的颜色
1038
+ }],
1039
+ global: false, // 缺省为 false
1040
+
1041
+ },
1042
+ },
1043
+ }
1044
+ ]
1045
+ };
1046
+ if(bottom){
1047
+ option.grid.bottom=bottom
1048
+ }
1049
+ const echartRef=useRef()
1050
+ useEffect(() => {
1051
+ if (echartRef.current) {
1052
+ const ins = echartRef.current.getEchartsInstance();
1053
+ ins.setOption(
1054
+ option
1055
+ );
1056
+ }
1057
+ }, [props]);
1058
+ return (
1059
+ <ReactECharts ref={echartRef} option={option} style={{ height: h,width:w}} />
1060
+ );
1061
+ }
1062
+
1063
+ function TsingrocMonthPick(props){
1064
+ const {value,onChange:propsOnChange,dataType ='month',className,popupClassName}=props
1065
+ const [dateValue,setDateValue]=useState(value)
1066
+ const quickGo=(type)=>{
1067
+ let temp = dateValue.add(type, dataType)
1068
+ setDateValue(temp)
1069
+ propsOnChange && propsOnChange(temp)
1070
+ }
1071
+ const onChange=(value)=>{
1072
+ setDateValue(value)
1073
+ propsOnChange && propsOnChange(value)
1074
+ }
1075
+ return <div>
1076
+ <div onClick={() => { quickGo(-1) }} className={`quick-jump ${className}`} style={{borderRight: 'none', borderRadius: '2px 0 0 2px' }}>
1077
+ <LeftOutlined className="quick-icon" />
1078
+ </div>
1079
+ {
1080
+ dataType === 'day' && <DatePicker value={dateValue} onChange={onChange} />
1081
+ }
1082
+ {
1083
+ dataType === 'week' && <DatePicker value={dateValue} onChange={onChange} picker="week" />
1084
+ }
1085
+ {
1086
+ dataType === 'month' && <DatePicker popupClassName={popupClassName} className={className} value={dateValue} onChange={onChange} picker="month" />
1087
+ }
1088
+ <div onClick={() => { quickGo(1) }} className={`quick-jump ${className}`} style={{ borderLeft:'none', borderRadius: '0 2px 2px 0' }}>
1089
+ <RightOutlined className="quick-icon" />
1090
+ </div>
1091
+
1092
+ </div>
1093
+ }
1094
+
1095
+ function ImageBac(props){
1096
+ const {children, width='100%',height='100%',url="",style,}=props
1097
+ let relativeUrl=''
1098
+ if(url.startsWith('@')){
1099
+ relativeUrl=url.replace('@','../../../../..')
1100
+ }else{
1101
+ relativeUrl=url
1102
+ }
1103
+ return <div style={{...style,backgroundSize:'100% 100%',width:width,height:height,backgroundImage:`url('${relativeUrl}')`}}>
1104
+ {children}
1105
+ </div>
1106
+ }
1107
+
1108
+ export {
1109
+ ImageBac,
1110
+ EchartMult,
1111
+ RiskEchartMult,
1112
+ TsingrocDatePick,
1113
+ RadarEchart,
1114
+ TsingrocPie,
1115
+ CircularProgress,
1116
+ WindFlow,
1117
+ BoxplotEchart,
1118
+ EchartJustOneLine,
1119
+ TsingrocMonthPick
1120
+ }