st-comp 0.0.244 → 0.0.246
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.
- package/es/ConfigProvider.cjs +1 -1
- package/es/ConfigProvider.js +1 -1
- package/es/CustomFunction.cjs +2 -2
- package/es/CustomFunction.js +262 -263
- package/es/FactorWarning.cjs +1 -1
- package/es/FactorWarning.js +1 -1
- package/es/Kline.cjs +1 -1
- package/es/Kline.js +1 -1
- package/es/KlineBasic.cjs +1 -1
- package/es/KlineBasic.js +574 -606
- package/es/KlineNew.cjs +1 -1
- package/es/KlineNew.js +1 -1
- package/es/KlinePlus.cjs +1 -1
- package/es/KlinePlus.js +1 -1
- package/es/MonacoEditor.cjs +1 -1
- package/es/MonacoEditor.js +3 -3
- package/es/Pie.cjs +1 -1
- package/es/Pie.js +1 -1
- package/es/User.cjs +1 -1
- package/es/User.js +1 -1
- package/es/VarSelectDialog.cjs +1 -1
- package/es/VarSelectDialog.js +2 -2
- package/es/VarietyAutoComplete.cjs +1 -1
- package/es/VarietyAutoComplete.js +1 -1
- package/es/VarietySearch.cjs +20 -20
- package/es/VarietySearch.js +2995 -3000
- package/es/{VarietySelect-031bf077.cjs → VarietySelect-2fd501da.cjs} +1 -1
- package/es/{VarietySelect-ae0c48b2.js → VarietySelect-5a9dd50b.js} +1 -1
- package/es/{index-1f939868.cjs → index-2375023e.cjs} +2 -2
- package/es/{index-edabe380.js → index-7ed0999e.js} +5487 -5574
- package/es/{index-2c456130.cjs → index-8901a38c.cjs} +40 -40
- package/es/{index-4b01552e.js → index-ac98a4d8.js} +3 -3
- package/es/{python-7ce6f0b1.js → python-a914569a.js} +3 -3
- package/es/{python-c8abd4f5.cjs → python-c67c8901.cjs} +1 -1
- package/es/style.css +1 -1
- package/lib/bundle.js +1 -1
- package/lib/bundle.umd.cjs +223 -223
- package/lib/{index-200db55b.js → index-2a325d42.js} +34655 -34779
- package/lib/{python-9540022d.js → python-eb65d93b.js} +1 -1
- package/lib/style.css +1 -1
- package/package.json +1 -1
- package/packages/CustomFunction/index.vue +2 -4
- package/packages/KlineBasic/index.vue +518 -497
- package/packages/VarietySearch/components/CompositeOrder/index.vue +2 -4
- package/packages/VarietySearch/components/FactorScreen/index.vue +5 -7
- package/packages/VarietySearch/index.vue +4 -6
- package/src/pages/KlineBasic/api.js +7 -4
- package/src/pages/KlineBasic/index.vue +346 -56
- package/src/pages/VarietySearch/index.vue +7 -2
|
@@ -1,108 +1,7 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div
|
|
3
|
-
class="klineBasic"
|
|
4
|
-
@mousemove="isHover = true"
|
|
5
|
-
@mouseout="isHover = false"
|
|
6
|
-
>
|
|
7
|
-
<div class="klineBasic-tips">
|
|
8
|
-
<KlineTips
|
|
9
|
-
:variety="variety"
|
|
10
|
-
:data="chartData"
|
|
11
|
-
:activeIndex="activeIndex"
|
|
12
|
-
/>
|
|
13
|
-
</div>
|
|
14
|
-
<div
|
|
15
|
-
class="klineBasic-main"
|
|
16
|
-
:style="{ height: config.showSubChart ? '70%' : '100%' }"
|
|
17
|
-
>
|
|
18
|
-
<Contextmenu @closeContextMenuCallBack="closeContextMenuCallBack">
|
|
19
|
-
<div
|
|
20
|
-
ref="klineBasicMainRef"
|
|
21
|
-
style="height: 100%"
|
|
22
|
-
></div>
|
|
23
|
-
<template #popover>
|
|
24
|
-
<el-menu
|
|
25
|
-
:style="{
|
|
26
|
-
borderRadius: '4px',
|
|
27
|
-
overflow: 'hidden',
|
|
28
|
-
background: '#fff',
|
|
29
|
-
borderRight: 0,
|
|
30
|
-
}"
|
|
31
|
-
>
|
|
32
|
-
<el-menu-item
|
|
33
|
-
v-for="item in menuData"
|
|
34
|
-
style="height: 36px"
|
|
35
|
-
:key="item.key"
|
|
36
|
-
:index="item.key"
|
|
37
|
-
@click="menuClick(item)"
|
|
38
|
-
>
|
|
39
|
-
{{ item.label }}
|
|
40
|
-
</el-menu-item>
|
|
41
|
-
</el-menu>
|
|
42
|
-
</template>
|
|
43
|
-
</Contextmenu>
|
|
44
|
-
</div>
|
|
45
|
-
<div
|
|
46
|
-
class="klineBasic-sub"
|
|
47
|
-
v-if="config.showSubChart"
|
|
48
|
-
>
|
|
49
|
-
<KlineSub
|
|
50
|
-
ref="klineSubRef"
|
|
51
|
-
v-model="subIndicator"
|
|
52
|
-
:data="chartData"
|
|
53
|
-
:cycle="cycle"
|
|
54
|
-
:activeIndex="activeIndex"
|
|
55
|
-
:subIndicatorList="indicatorStore?.subIndicatorList"
|
|
56
|
-
/>
|
|
57
|
-
</div>
|
|
58
|
-
<div
|
|
59
|
-
class="klineBasic-empty"
|
|
60
|
-
v-if="isEmpty"
|
|
61
|
-
>
|
|
62
|
-
<el-empty
|
|
63
|
-
class="klineBasic-empty-content"
|
|
64
|
-
description="暂无数据"
|
|
65
|
-
/>
|
|
66
|
-
</div>
|
|
67
|
-
<div
|
|
68
|
-
class="klineBasic-error"
|
|
69
|
-
v-if="isError"
|
|
70
|
-
>
|
|
71
|
-
<div class="klineBasic-error-content">加载失败,请刷新重试</div>
|
|
72
|
-
<div style="text-align: center">
|
|
73
|
-
<el-button @click="getMainData">刷新</el-button>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
<!-- 画线预警-修改价格弹窗 -->
|
|
78
|
-
<el-dialog
|
|
79
|
-
v-model="warningLineChangeVisible"
|
|
80
|
-
title="画线预警-修改价格"
|
|
81
|
-
width="30%"
|
|
82
|
-
align-center
|
|
83
|
-
>
|
|
84
|
-
<span style="margin-right: 10px">预警价格:</span>
|
|
85
|
-
<el-input-number
|
|
86
|
-
v-model="warningLineChangeValue"
|
|
87
|
-
placeholder="输入预警价格"
|
|
88
|
-
/>
|
|
89
|
-
<template #footer>
|
|
90
|
-
<span class="dialog-footer">
|
|
91
|
-
<el-button @click="warningLineChangeVisible = false">取消</el-button>
|
|
92
|
-
<el-button
|
|
93
|
-
type="primary"
|
|
94
|
-
@click="changeWarningLine"
|
|
95
|
-
>确定</el-button
|
|
96
|
-
>
|
|
97
|
-
</span>
|
|
98
|
-
</template>
|
|
99
|
-
</el-dialog>
|
|
100
|
-
</template>
|
|
101
|
-
|
|
102
1
|
<script setup>
|
|
103
|
-
import { onMounted, onUnmounted, ref, watch, computed } from "vue";
|
|
104
|
-
import * as echarts from "echarts";
|
|
105
2
|
import dayjs from "dayjs";
|
|
3
|
+
import * as echarts from "echarts";
|
|
4
|
+
import { onMounted, onUnmounted, ref, watch, computed, nextTick } from "vue";
|
|
106
5
|
import { initRequestByEnv, getKlineBasic, getKline, getWarningLine, addWarningLine, updateWarningLine, deleteWarningLine } from "./api";
|
|
107
6
|
import { addResizeListener } from "st-func";
|
|
108
7
|
import { getMainOptions, getWarningLineOptions } from "./utils";
|
|
@@ -114,6 +13,9 @@ const defaultMenuData = [{ label: "画线预警", key: "drawWarningLine" }];
|
|
|
114
13
|
|
|
115
14
|
let resizeRo = null; // dom元素监听事件
|
|
116
15
|
let mainChartIns = null; // 主图实例
|
|
16
|
+
let renderFrameId = null; // 渲染帧ID
|
|
17
|
+
let drawLineFrameId = null; // 绘线帧ID
|
|
18
|
+
let brushFrameId = null; // 区域框选帧ID
|
|
117
19
|
|
|
118
20
|
let highlightTimer; // 高亮事件定时器
|
|
119
21
|
let mainDataZoomTimer; // datazoom事件定时器
|
|
@@ -127,31 +29,31 @@ const props = defineProps({
|
|
|
127
29
|
variety: {
|
|
128
30
|
type: [String, Number],
|
|
129
31
|
default: () => null,
|
|
130
|
-
},
|
|
32
|
+
},
|
|
131
33
|
varietyName: {
|
|
132
34
|
type: [String, Number],
|
|
133
35
|
default: () => null,
|
|
134
|
-
},
|
|
36
|
+
},
|
|
135
37
|
featureId: {
|
|
136
38
|
type: [String, Number],
|
|
137
39
|
default: () => null,
|
|
138
|
-
},
|
|
40
|
+
},
|
|
139
41
|
featureType: {
|
|
140
42
|
type: [String, Number],
|
|
141
43
|
default: () => null,
|
|
142
|
-
},
|
|
44
|
+
},
|
|
143
45
|
cycle: {
|
|
144
46
|
type: [String, Number],
|
|
145
47
|
default: () => null,
|
|
146
|
-
},
|
|
48
|
+
},
|
|
147
49
|
mainIndicator: {
|
|
148
50
|
type: String,
|
|
149
51
|
default: () => "",
|
|
150
|
-
},
|
|
52
|
+
},
|
|
151
53
|
indicatorStore: {
|
|
152
54
|
type: Object,
|
|
153
55
|
default: () => null,
|
|
154
|
-
},
|
|
56
|
+
},
|
|
155
57
|
startTime: {
|
|
156
58
|
type: String,
|
|
157
59
|
default: () => null,
|
|
@@ -163,78 +65,54 @@ const props = defineProps({
|
|
|
163
65
|
right: {
|
|
164
66
|
type: [String, Number],
|
|
165
67
|
default: 1,
|
|
166
|
-
},
|
|
68
|
+
},
|
|
167
69
|
config: {
|
|
168
70
|
type: Object,
|
|
169
71
|
default: () => ({}),
|
|
170
|
-
},
|
|
72
|
+
},
|
|
171
73
|
env: {
|
|
172
74
|
type: Object,
|
|
173
75
|
default: () => ({}),
|
|
174
|
-
},
|
|
76
|
+
},
|
|
175
77
|
brushRange: {
|
|
176
78
|
type: [Array, null],
|
|
177
79
|
default: () => null,
|
|
178
|
-
},
|
|
80
|
+
},
|
|
179
81
|
});
|
|
180
82
|
|
|
181
|
-
const isHover = ref(false);
|
|
182
|
-
const isEmpty = ref(false);
|
|
183
|
-
const isError = ref(false);
|
|
83
|
+
const isHover = ref(false);
|
|
84
|
+
const isEmpty = ref(false);
|
|
85
|
+
const isError = ref(false);
|
|
184
86
|
|
|
185
|
-
const klineBasicMainRef = ref(null);
|
|
186
|
-
const klineSubRef = ref(null);
|
|
187
|
-
const subIndicator = ref("VOL");
|
|
188
|
-
const activeIndex = ref(0);
|
|
87
|
+
const klineBasicMainRef = ref(null);
|
|
88
|
+
const klineSubRef = ref(null);
|
|
89
|
+
const subIndicator = ref("VOL");
|
|
90
|
+
const activeIndex = ref(0);
|
|
189
91
|
|
|
190
|
-
const chartData = ref({});
|
|
92
|
+
const chartData = ref({});
|
|
93
|
+
const warningLineData = ref([]);
|
|
94
|
+
const warningItem = ref({});
|
|
95
|
+
const warningLineChangeVisible = ref(false);
|
|
96
|
+
const warningLineChangeValue = ref("");
|
|
191
97
|
|
|
192
|
-
const
|
|
193
|
-
const warningItem = ref({}); // 右键点击的预警线
|
|
194
|
-
const warningLineChangeVisible = ref(false); // 修改预警线价格弹窗visible
|
|
195
|
-
const warningLineChangeValue = ref(""); // 修改预警线价格弹窗value
|
|
196
|
-
|
|
197
|
-
const menuData = ref([...defaultMenuData]); // 右键菜单
|
|
98
|
+
const menuData = ref([...defaultMenuData]);
|
|
198
99
|
|
|
199
100
|
const config = computed(() => {
|
|
200
101
|
return {
|
|
201
|
-
defaultShowCounts: 500,
|
|
202
|
-
addCounts: 2000,
|
|
203
|
-
maxShowCounts: 5000,
|
|
204
|
-
loadCheckCounts: 500,
|
|
205
|
-
showSubChart: true,
|
|
206
|
-
gridTop: 48,
|
|
207
|
-
gridLeft: 80,
|
|
208
|
-
gridRight: 50,
|
|
209
|
-
showWarningLine: true,
|
|
210
|
-
getFactorData: true,
|
|
102
|
+
defaultShowCounts: 500,
|
|
103
|
+
addCounts: 2000,
|
|
104
|
+
maxShowCounts: 5000,
|
|
105
|
+
loadCheckCounts: 500,
|
|
106
|
+
showSubChart: true,
|
|
107
|
+
gridTop: 48,
|
|
108
|
+
gridLeft: 80,
|
|
109
|
+
gridRight: 50,
|
|
110
|
+
showWarningLine: true,
|
|
111
|
+
getFactorData: true,
|
|
211
112
|
...props.config,
|
|
212
113
|
};
|
|
213
114
|
});
|
|
214
115
|
|
|
215
|
-
watch(
|
|
216
|
-
() => [props.variety, props.cycle, props.mainIndicator, subIndicator.value, props.indicatorStore?.filterIndicator, props.indicatorStore?.customIndicator],
|
|
217
|
-
() => {
|
|
218
|
-
getMainData();
|
|
219
|
-
},
|
|
220
|
-
{ deep: true }
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
onMounted(() => {
|
|
224
|
-
initRequestByEnv(props.env);
|
|
225
|
-
getMainData();
|
|
226
|
-
window.addEventListener("keydown", handleKeyDownEvent);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
onUnmounted(() => {
|
|
230
|
-
mainChartIns?.off("datazoom");
|
|
231
|
-
mainChartIns?.off("highlight");
|
|
232
|
-
mainChartIns?.off("globalout");
|
|
233
|
-
mainChartIns?.dispose();
|
|
234
|
-
resizeRo?.dispose();
|
|
235
|
-
window.removeEventListener("keydown", handleKeyDownEvent);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
116
|
// 初始化图表
|
|
239
117
|
const initChart = () => {
|
|
240
118
|
if (mainChartIns) return;
|
|
@@ -246,136 +124,225 @@ const initChart = () => {
|
|
|
246
124
|
resizeRo = addResizeListener(klineBasicMainRef.value);
|
|
247
125
|
resizeRo.listen(() => {
|
|
248
126
|
requestAnimationFrame(() => {
|
|
249
|
-
mainChartIns
|
|
127
|
+
mainChartIns?.resize();
|
|
128
|
+
drawLine(); // resize后重绘预警线
|
|
250
129
|
});
|
|
251
130
|
});
|
|
252
131
|
};
|
|
253
|
-
//
|
|
254
|
-
const
|
|
255
|
-
if (
|
|
256
|
-
|
|
257
|
-
const { data: xAxisData } = xAxis?.[0] ?? { data: [] };
|
|
258
|
-
let { startValue, endValue } = dataZoom?.[0] ?? {};
|
|
259
|
-
if (!xAxisData?.length) return;
|
|
260
|
-
// 键位cb
|
|
261
|
-
switch (code) {
|
|
262
|
-
// ↑ 放大
|
|
263
|
-
case "ArrowUp": {
|
|
264
|
-
if (endValue - startValue < 5) return;
|
|
265
|
-
const diff = Math.floor((endValue - startValue) / 2) + 1;
|
|
266
|
-
startValue = startValue + diff;
|
|
267
|
-
if (endValue - startValue < 5) startValue = endValue - 4;
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
// ↓ 缩小
|
|
271
|
-
case "ArrowDown": {
|
|
272
|
-
const diff = Math.min(500, endValue - startValue);
|
|
273
|
-
startValue = startValue - diff - 1;
|
|
274
|
-
break;
|
|
275
|
-
}
|
|
276
|
-
// ← 左移
|
|
277
|
-
case "ArrowLeft": {
|
|
278
|
-
if (startValue > 0) {
|
|
279
|
-
startValue -= 1;
|
|
280
|
-
endValue -= 1;
|
|
281
|
-
}
|
|
282
|
-
if (activeIndex.value > 0) {
|
|
283
|
-
activeIndex.value -= 1;
|
|
284
|
-
}
|
|
285
|
-
break;
|
|
286
|
-
}
|
|
287
|
-
// → 右移
|
|
288
|
-
case "ArrowRight": {
|
|
289
|
-
if (endValue < xAxisData.length - 1) {
|
|
290
|
-
startValue += 1;
|
|
291
|
-
endValue += 1;
|
|
292
|
-
}
|
|
293
|
-
if (isHover.value && activeIndex.value < xAxisData.length - 1) {
|
|
294
|
-
activeIndex.value += 1;
|
|
295
|
-
}
|
|
296
|
-
break;
|
|
297
|
-
}
|
|
132
|
+
// 隐藏loading
|
|
133
|
+
const hideLoading = () => {
|
|
134
|
+
if (mainChartIns && !mainChartIns.isDisposed()) {
|
|
135
|
+
mainChartIns.hideLoading();
|
|
298
136
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// 图表渲染: 全部
|
|
140
|
+
const scheduleRender = (keepDataZoom = false) => {
|
|
141
|
+
// 取消正在进行的渲染
|
|
142
|
+
if (renderFrameId) {
|
|
143
|
+
cancelAnimationFrame(renderFrameId);
|
|
144
|
+
renderFrameId = null;
|
|
145
|
+
}
|
|
146
|
+
// 第一帧:渲染主图
|
|
147
|
+
renderFrameId = requestAnimationFrame(() => {
|
|
148
|
+
drawMain(keepDataZoom);
|
|
149
|
+
// 第二帧:渲染副图
|
|
150
|
+
renderFrameId = requestAnimationFrame(() => {
|
|
151
|
+
drawSub();
|
|
152
|
+
// 第三帧:渲染预警线
|
|
153
|
+
renderFrameId = requestAnimationFrame(() => {
|
|
154
|
+
drawLine();
|
|
155
|
+
renderFrameId = null;
|
|
156
|
+
});
|
|
157
|
+
});
|
|
315
158
|
});
|
|
316
159
|
};
|
|
317
|
-
//
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
160
|
+
// 图表渲染: 主图
|
|
161
|
+
const drawMain = (keepDataZoom = false) => {
|
|
162
|
+
if (!mainChartIns || mainChartIns.isDisposed()) return;
|
|
163
|
+
|
|
164
|
+
const { time } = chartData.value;
|
|
165
|
+
const { defaultShowCounts } = config.value;
|
|
166
|
+
|
|
167
|
+
let startValue = time.length - 1 - defaultShowCounts;
|
|
168
|
+
let endValue = time.length - 1;
|
|
169
|
+
|
|
170
|
+
// 保持缩放位置
|
|
171
|
+
if (keepDataZoom && mainChartIns) {
|
|
172
|
+
const originOption = mainChartIns.getOption();
|
|
173
|
+
const originTime = originOption?.xAxis?.[0]?.data;
|
|
174
|
+
const originDataZoom = originOption?.dataZoom?.[0];
|
|
175
|
+
|
|
176
|
+
if (originTime?.length && originDataZoom) {
|
|
177
|
+
const originStartTime = originTime[originDataZoom.startValue];
|
|
178
|
+
const originEndTime = originTime[originDataZoom.endValue];
|
|
179
|
+
if (originStartTime && originEndTime) {
|
|
180
|
+
// startValue: 从前往后找第一个 >= 原始开始时间的索引
|
|
181
|
+
const foundStartIndex = time.findIndex((item) => new Date(item).getTime() >= new Date(originStartTime).getTime());
|
|
182
|
+
// endValue: 从后往前找最后一个 <= 原始结束时间的索引
|
|
183
|
+
const foundEndIndex = time.findLastIndex((item) => new Date(item).getTime() <= new Date(originEndTime).getTime());
|
|
184
|
+
// 使用找到的索引,如果没找到则使用默认值
|
|
185
|
+
startValue = foundStartIndex !== -1 ? foundStartIndex : 0;
|
|
186
|
+
endValue = foundEndIndex !== -1 ? foundEndIndex : time.length - 1;
|
|
187
|
+
// 确保 startValue 不大于 endValue
|
|
188
|
+
if (startValue > endValue) {
|
|
189
|
+
// 如果范围异常,以结束索引为准,向前取一定数量
|
|
190
|
+
startValue = Math.max(0, endValue - Math.min(100, time.length - 1));
|
|
331
191
|
}
|
|
332
|
-
drawLine();
|
|
333
192
|
}
|
|
334
|
-
clearTimeout(mainDataZoomTimer);
|
|
335
|
-
}, 100);
|
|
336
|
-
});
|
|
337
|
-
// 高亮事件
|
|
338
|
-
mainChartIns?.on("highlight", (data) => {
|
|
339
|
-
let index = data.dataIndex || -1;
|
|
340
|
-
if (data.batch) {
|
|
341
|
-
index = typeof data?.batch[0]?.dataIndex === "number" ? data?.batch[0]?.dataIndex : -1;
|
|
342
193
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
activeIndex.value = index;
|
|
355
|
-
}, 30);
|
|
194
|
+
}
|
|
195
|
+
const options = getMainOptions(chartData.value, config.value, startValue, endValue, props.cycle);
|
|
196
|
+
|
|
197
|
+
requestAnimationFrame(() => {
|
|
198
|
+
mainChartIns?.setOption(options, true);
|
|
199
|
+
activeIndex.value = endValue;
|
|
200
|
+
|
|
201
|
+
// 下一帧绘制brush
|
|
202
|
+
requestAnimationFrame(() => {
|
|
203
|
+
drawBrush();
|
|
204
|
+
});
|
|
356
205
|
});
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
206
|
+
};
|
|
207
|
+
// 图表渲染: 副图
|
|
208
|
+
const drawSub = () => {
|
|
209
|
+
if (!config.value.showSubChart || !klineSubRef.value) return;
|
|
210
|
+
|
|
211
|
+
const { time } = chartData.value;
|
|
212
|
+
const { gridLeft, gridRight, maxShowCounts, defaultShowCounts } = config.value;
|
|
213
|
+
let startValue = time.length - 1 - defaultShowCounts;
|
|
214
|
+
let endValue = time.length - 1;
|
|
215
|
+
|
|
216
|
+
// 从主图获取当前的dataZoom位置
|
|
217
|
+
if (mainChartIns && !mainChartIns.isDisposed()) {
|
|
218
|
+
const option = mainChartIns.getOption();
|
|
219
|
+
if (option?.dataZoom?.[0]) {
|
|
220
|
+
const { startValue: cacheStart, endValue: cacheEnd } = option.dataZoom[0];
|
|
221
|
+
if (cacheStart !== undefined && cacheEnd !== undefined) {
|
|
222
|
+
startValue = cacheStart;
|
|
223
|
+
endValue = cacheEnd;
|
|
367
224
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
requestAnimationFrame(() => {
|
|
229
|
+
klineSubRef.value.draw({ startValue, endValue, maxValueSpan: maxShowCounts }, { gridLeft, gridRight });
|
|
371
230
|
});
|
|
372
231
|
};
|
|
232
|
+
// 图表渲染: 预警线
|
|
233
|
+
const drawLine = () => {
|
|
234
|
+
if (!mainChartIns) return;
|
|
373
235
|
|
|
374
|
-
|
|
236
|
+
// 取消正在进行的绘线
|
|
237
|
+
if (drawLineFrameId) {
|
|
238
|
+
cancelAnimationFrame(drawLineFrameId);
|
|
239
|
+
drawLineFrameId = null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
drawLineFrameId = requestAnimationFrame(() => {
|
|
243
|
+
const mainChartOption = mainChartIns?.getOption();
|
|
244
|
+
if (!mainChartOption) return;
|
|
245
|
+
const width = mainChartIns?.getWidth();
|
|
246
|
+
const height = mainChartIns?.getHeight();
|
|
247
|
+
if (!width || !height) return;
|
|
248
|
+
|
|
249
|
+
mainChartIns?.setOption(
|
|
250
|
+
{
|
|
251
|
+
graphic: getWarningLineOptions(mainChartIns, warningLineData.value, props, config.value, () => {
|
|
252
|
+
updateWarningLineAndDraw();
|
|
253
|
+
}),
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
notMerge: false,
|
|
257
|
+
replaceMerge: ["graphic"],
|
|
258
|
+
lazyUpdate: true,
|
|
259
|
+
},
|
|
260
|
+
);
|
|
261
|
+
drawLineFrameId = null;
|
|
262
|
+
});
|
|
263
|
+
};
|
|
264
|
+
// 图表渲染: 区域框选
|
|
265
|
+
const drawBrush = () => {
|
|
266
|
+
if (brushFrameId) {
|
|
267
|
+
cancelAnimationFrame(brushFrameId);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
brushFrameId = requestAnimationFrame(() => {
|
|
271
|
+
const { time } = chartData.value;
|
|
272
|
+
if (!props.brushRange || !time?.length || !mainChartIns) return;
|
|
273
|
+
|
|
274
|
+
let startTime = dayjs(props.brushRange[0]).format("YYYY-MM-DD 00:00:00");
|
|
275
|
+
let endTime = dayjs(props.brushRange[1]).format("YYYY-MM-DD 23:59:59");
|
|
276
|
+
let brushStartTime = null;
|
|
277
|
+
let brushEndTime = null;
|
|
278
|
+
|
|
279
|
+
switch (String(props.cycle)) {
|
|
280
|
+
case "6": {
|
|
281
|
+
brushStartTime = time.find((item) => dayjs(item).format("YYYY-MM-DD") === dayjs(startTime).format("YYYY-MM-DD"));
|
|
282
|
+
brushEndTime = brushStartTime;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
case "7": {
|
|
286
|
+
brushStartTime = time.find((item) => dayjs(item).day(5).format("YYYY-MM-DD") === dayjs(startTime).day(5).format("YYYY-MM-DD"));
|
|
287
|
+
brushEndTime = time.findLast((item) => dayjs(item).day(5).format("YYYY-MM-DD") === dayjs(endTime).day(5).format("YYYY-MM-DD"));
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
case "8": {
|
|
291
|
+
brushStartTime = time.find((item) => dayjs(item).endOf("month").format("YYYY-MM-DD") === dayjs(startTime).endOf("month").format("YYYY-MM-DD"));
|
|
292
|
+
brushEndTime = time.findLast((item) => dayjs(item).endOf("month").format("YYYY-MM-DD") === dayjs(endTime).endOf("month").format("YYYY-MM-DD"));
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
default: {
|
|
296
|
+
brushStartTime = time.find((item) => {
|
|
297
|
+
const condition1 = dayjs(item).format("YYYY-MM-DD") === dayjs(startTime).format("YYYY-MM-DD");
|
|
298
|
+
const condition2 = new Date(item).getTime() >= new Date(startTime).getTime();
|
|
299
|
+
return condition1 && condition2;
|
|
300
|
+
});
|
|
301
|
+
brushEndTime = time.findLast((item) => {
|
|
302
|
+
const condition1 = dayjs(item).format("YYYY-MM-DD") === dayjs(endTime).format("YYYY-MM-DD");
|
|
303
|
+
const condition2 = new Date(item).getTime() <= new Date(endTime).getTime();
|
|
304
|
+
return condition1 && condition2;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (brushStartTime && brushEndTime) {
|
|
310
|
+
mainChartIns.dispatchAction({
|
|
311
|
+
type: "brush",
|
|
312
|
+
areas: [
|
|
313
|
+
{
|
|
314
|
+
brushType: "lineX",
|
|
315
|
+
coordRange: [brushStartTime, brushEndTime],
|
|
316
|
+
xAxisIndex: 0,
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
brushFrameId = null;
|
|
322
|
+
});
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// 获取主数据
|
|
326
|
+
const getMainData = async () => {
|
|
375
327
|
try {
|
|
376
328
|
if (!props.variety || !props.cycle) return;
|
|
329
|
+
|
|
377
330
|
const { variety, featureId, cycle, indicatorStore, mainIndicator, right, startTime, endTime } = props;
|
|
378
331
|
const { defaultShowCounts, addCounts, showWarningLine, getFactorData } = config.value;
|
|
332
|
+
|
|
333
|
+
// 显示loading
|
|
334
|
+
initChart();
|
|
335
|
+
|
|
336
|
+
if (mainChartIns && !mainChartIns.isDisposed()) {
|
|
337
|
+
mainChartIns.showLoading({
|
|
338
|
+
text: "加载中...",
|
|
339
|
+
color: "#409eff",
|
|
340
|
+
textColor: "#fff",
|
|
341
|
+
maskColor: "rgba(0, 0, 0, 0.7)",
|
|
342
|
+
zlevel: 0,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
379
346
|
const params = {
|
|
380
347
|
variety,
|
|
381
348
|
featureId,
|
|
@@ -386,53 +353,58 @@ const getMainData = async () => {
|
|
|
386
353
|
showWarningLine,
|
|
387
354
|
getFactorData,
|
|
388
355
|
};
|
|
356
|
+
|
|
389
357
|
if (startTime && endTime) {
|
|
390
|
-
// 开始时间+结束时间
|
|
391
358
|
params.startTime = startTime;
|
|
392
359
|
params.endTime = endTime;
|
|
393
360
|
} else if (startTime) {
|
|
394
|
-
// 开始时间
|
|
395
361
|
params.startTime = startTime;
|
|
396
362
|
params.limit = defaultShowCounts + addCounts;
|
|
397
363
|
} else if (endTime) {
|
|
398
|
-
// 结束时间
|
|
399
364
|
params.endTime = endTime;
|
|
400
365
|
params.limit = defaultShowCounts + addCounts;
|
|
401
366
|
} else {
|
|
402
|
-
// 未传入时间,使用最新时间作为结束时间请求数据
|
|
403
367
|
params.endTime = dayjs().add(1, "hour").format("YYYY-MM-DD HH:mm:ss");
|
|
404
368
|
params.limit = defaultShowCounts + addCounts;
|
|
405
369
|
}
|
|
370
|
+
|
|
406
371
|
const res = await getKlineBasic(params);
|
|
372
|
+
|
|
407
373
|
if (!res?.body?.kline?.time?.length) {
|
|
408
374
|
isEmpty.value = true;
|
|
409
375
|
isError.value = false;
|
|
376
|
+
hideLoading();
|
|
410
377
|
return;
|
|
411
|
-
} else {
|
|
412
|
-
isEmpty.value = false;
|
|
413
|
-
isError.value = false;
|
|
414
378
|
}
|
|
379
|
+
|
|
380
|
+
isEmpty.value = false;
|
|
381
|
+
isError.value = false;
|
|
415
382
|
chartData.value = res?.body?.kline;
|
|
416
383
|
warningLineData.value = res?.body?.warningLine || [];
|
|
417
|
-
|
|
418
|
-
|
|
384
|
+
|
|
385
|
+
// 使用RAF链式渲染
|
|
386
|
+
await nextTick();
|
|
387
|
+
scheduleRender(false);
|
|
388
|
+
|
|
419
389
|
if (getFactorData) {
|
|
420
390
|
emit("getFactorData", res?.body?.factor);
|
|
421
391
|
}
|
|
392
|
+
|
|
393
|
+
hideLoading();
|
|
422
394
|
} catch (err) {
|
|
395
|
+
hideLoading();
|
|
423
396
|
isError.value = true;
|
|
424
397
|
isEmpty.value = false;
|
|
425
398
|
throw new Error(err);
|
|
426
399
|
}
|
|
427
400
|
};
|
|
428
|
-
|
|
429
|
-
// 加载历史/未来数据
|
|
401
|
+
// 加载历史数据
|
|
430
402
|
const getMoreData = async (type) => {
|
|
431
403
|
const { variety, cycle, indicatorStore, mainIndicator, right } = props;
|
|
432
404
|
const { addCounts } = config.value;
|
|
433
405
|
const { time } = chartData.value;
|
|
406
|
+
|
|
434
407
|
if (type === "history") {
|
|
435
|
-
// 加载历史数据
|
|
436
408
|
const res = await getKline({
|
|
437
409
|
variety,
|
|
438
410
|
cycle,
|
|
@@ -446,261 +418,180 @@ const getMoreData = async (type) => {
|
|
|
446
418
|
chartData.value = {
|
|
447
419
|
time: [...res.body.time, ...chartData.value.time.slice(1)],
|
|
448
420
|
data: [...res.body.data, ...chartData.value.data.slice(1)],
|
|
449
|
-
mainIndicator: chartData.value.mainIndicator.map((item, index) => {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
...item,
|
|
458
|
-
data: [...(res.body.subIndicator[index]?.data ?? []), ...item.data.slice(1)],
|
|
459
|
-
};
|
|
460
|
-
}),
|
|
421
|
+
mainIndicator: chartData.value.mainIndicator.map((item, index) => ({
|
|
422
|
+
...item,
|
|
423
|
+
data: [...(res.body.mainIndicator[index]?.data ?? []), ...item.data.slice(1)],
|
|
424
|
+
})),
|
|
425
|
+
subIndicator: chartData.value.subIndicator.map((item, index) => ({
|
|
426
|
+
...item,
|
|
427
|
+
data: [...(res.body.subIndicator[index]?.data ?? []), ...item.data.slice(1)],
|
|
428
|
+
})),
|
|
461
429
|
};
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
430
|
+
console.log("触发了加载更多数据");
|
|
431
|
+
// 使用RAF链式渲染,保持当前缩放位置
|
|
432
|
+
await nextTick();
|
|
433
|
+
scheduleRender(true);
|
|
465
434
|
if (res.body.data.length < addCounts) {
|
|
466
435
|
isloadAllHistory = true;
|
|
436
|
+
console.log("历史数据已经全部加载完毕");
|
|
467
437
|
}
|
|
468
438
|
isLoadHistory = false;
|
|
469
|
-
} else {
|
|
470
|
-
// 加载未来数据
|
|
471
439
|
}
|
|
472
440
|
};
|
|
441
|
+
// 键盘事件
|
|
442
|
+
const handleKeyDownEvent = ({ code, ctrlKey }) => {
|
|
443
|
+
if (!(ctrlKey || isHover.value)) return;
|
|
444
|
+
const { xAxis, dataZoom } = mainChartIns.getOption();
|
|
445
|
+
const { data: xAxisData } = xAxis?.[0] ?? { data: [] };
|
|
446
|
+
let { startValue, endValue } = dataZoom?.[0] ?? {};
|
|
447
|
+
if (!xAxisData?.length) return;
|
|
473
448
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
mainChartIns.setOption(options, true);
|
|
492
|
-
activeIndex.value = endValue;
|
|
493
|
-
// 如果传入了刷选时间段
|
|
494
|
-
if (props.brushRange) {
|
|
495
|
-
let brushStartTime = null;
|
|
496
|
-
let brushEndTime = null;
|
|
497
|
-
switch (props.cycle) {
|
|
498
|
-
// 日
|
|
499
|
-
case "6": {
|
|
500
|
-
brushStartTime = time.find((item) => {
|
|
501
|
-
return new Date(item).getTime() >= new Date(props.brushRange[0]).getTime() || dayjs(item).format("YYYY-MM-DD") === dayjs(props.brushRange[0]).format("YYYY-MM-DD");
|
|
502
|
-
});
|
|
503
|
-
brushEndTime = time.findLast((item) => {
|
|
504
|
-
return new Date(item).getTime() <= new Date(props.brushRange[1]).getTime() || dayjs(item).format("YYYY-MM-DD") === dayjs(props.brushRange[1]).format("YYYY-MM-DD");
|
|
505
|
-
});
|
|
506
|
-
break;
|
|
449
|
+
switch (code) {
|
|
450
|
+
case "ArrowUp": {
|
|
451
|
+
if (endValue - startValue < 5) return;
|
|
452
|
+
const diff = Math.floor((endValue - startValue) / 2) + 1;
|
|
453
|
+
startValue = startValue + diff;
|
|
454
|
+
if (endValue - startValue < 5) startValue = endValue - 4;
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
case "ArrowDown": {
|
|
458
|
+
const diff = Math.min(500, endValue - startValue);
|
|
459
|
+
startValue = startValue - diff - 1;
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
case "ArrowLeft": {
|
|
463
|
+
if (startValue > 0) {
|
|
464
|
+
startValue -= 1;
|
|
465
|
+
endValue -= 1;
|
|
507
466
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
brushStartTime = time.find((item) => {
|
|
511
|
-
return dayjs(item).day(5).format("YYYY-MM-DD") === dayjs(props.brushRange[0]).day(5).format("YYYY-MM-DD");
|
|
512
|
-
});
|
|
513
|
-
brushEndTime = time.findLast((item) => {
|
|
514
|
-
return dayjs(item).day(5).format("YYYY-MM-DD") === dayjs(props.brushRange[1]).day(5).format("YYYY-MM-DD");
|
|
515
|
-
});
|
|
516
|
-
break;
|
|
467
|
+
if (activeIndex.value > 0) {
|
|
468
|
+
activeIndex.value -= 1;
|
|
517
469
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
return dayjs(item).endOf("month").format("YYYY-MM-DD") === dayjs(props.brushRange[1]).endOf("month").format("YYYY-MM-DD");
|
|
525
|
-
});
|
|
526
|
-
break;
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
case "ArrowRight": {
|
|
473
|
+
if (endValue < xAxisData.length - 1) {
|
|
474
|
+
startValue += 1;
|
|
475
|
+
endValue += 1;
|
|
527
476
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
return new Date(item).getTime() >= new Date(props.brushRange[0]).getTime();
|
|
531
|
-
});
|
|
532
|
-
brushEndTime = time.findLast((item) => {
|
|
533
|
-
return new Date(item).getTime() <= new Date(props.brushRange[1]).getTime();
|
|
534
|
-
});
|
|
477
|
+
if (isHover.value && activeIndex.value < xAxisData.length - 1) {
|
|
478
|
+
activeIndex.value += 1;
|
|
535
479
|
}
|
|
480
|
+
break;
|
|
536
481
|
}
|
|
537
|
-
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
requestAnimationFrame(() => {
|
|
485
|
+
mainChartIns.dispatchAction({ type: "dataZoom", startValue, endValue });
|
|
538
486
|
mainChartIns.dispatchAction({
|
|
539
|
-
type: "
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
brushType: "lineX",
|
|
543
|
-
coordRange: [brushStartTime, brushEndTime],
|
|
544
|
-
xAxisIndex: 0,
|
|
545
|
-
},
|
|
546
|
-
],
|
|
487
|
+
type: "updateAxisPointer",
|
|
488
|
+
seriesIndex: 0,
|
|
489
|
+
dataIndex: isHover.value ? activeIndex.value : null,
|
|
547
490
|
});
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
491
|
+
mainChartIns.dispatchAction({
|
|
492
|
+
type: "highlight",
|
|
493
|
+
dataIndex: isHover.value ? activeIndex.value : endValue,
|
|
494
|
+
});
|
|
495
|
+
});
|
|
553
496
|
};
|
|
497
|
+
// 图表事件
|
|
498
|
+
const addEventListener = () => {
|
|
499
|
+
mainChartIns?.on("datazoom", (params) => {
|
|
500
|
+
clearTimeout(mainDataZoomTimer);
|
|
501
|
+
mainDataZoomTimer = setTimeout(() => {
|
|
502
|
+
const { loadCheckCounts } = config.value;
|
|
503
|
+
if (mainChartIns?.getOption()?.dataZoom?.[0]) {
|
|
504
|
+
const { startValue } = mainChartIns?.getOption()?.dataZoom?.[0];
|
|
505
|
+
if (startValue < loadCheckCounts && !isLoadHistory && !isloadAllHistory) {
|
|
506
|
+
isLoadHistory = true;
|
|
507
|
+
getMoreData("history");
|
|
508
|
+
}
|
|
509
|
+
drawLine(); // 使用RAF优化的绘线
|
|
510
|
+
}
|
|
511
|
+
clearTimeout(mainDataZoomTimer);
|
|
512
|
+
}, 100);
|
|
513
|
+
});
|
|
554
514
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
{
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
}),
|
|
567
|
-
],
|
|
568
|
-
},
|
|
569
|
-
true
|
|
570
|
-
);
|
|
571
|
-
drawBrush();
|
|
572
|
-
};
|
|
515
|
+
mainChartIns?.on("highlight", (data) => {
|
|
516
|
+
let index = data.dataIndex || -1;
|
|
517
|
+
if (data.batch) {
|
|
518
|
+
index = typeof data?.batch[0]?.dataIndex === "number" ? data?.batch[0]?.dataIndex : -1;
|
|
519
|
+
}
|
|
520
|
+
clearTimeout(highlightTimer);
|
|
521
|
+
highlightTimer = setTimeout(() => {
|
|
522
|
+
activeIndex.value = index;
|
|
523
|
+
clearTimeout(highlightTimer);
|
|
524
|
+
}, 20);
|
|
525
|
+
});
|
|
573
526
|
|
|
574
|
-
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}
|
|
592
|
-
// 周
|
|
593
|
-
case "7": {
|
|
594
|
-
brushStartTime = time.find((item) => {
|
|
595
|
-
return dayjs(item).day(5).format("YYYY-MM-DD") === dayjs(startTime).day(5).format("YYYY-MM-DD");
|
|
596
|
-
});
|
|
597
|
-
brushEndTime = time.findLast((item) => {
|
|
598
|
-
return dayjs(item).day(5).format("YYYY-MM-DD") === dayjs(endTime).day(5).format("YYYY-MM-DD");
|
|
599
|
-
});
|
|
600
|
-
break;
|
|
601
|
-
}
|
|
602
|
-
// 月
|
|
603
|
-
case "8": {
|
|
604
|
-
brushStartTime = time.find((item) => {
|
|
605
|
-
return dayjs(item).endOf("month").format("YYYY-MM-DD") === dayjs(startTime).endOf("month").format("YYYY-MM-DD");
|
|
606
|
-
});
|
|
607
|
-
brushEndTime = time.findLast((item) => {
|
|
608
|
-
return dayjs(item).endOf("month").format("YYYY-MM-DD") === dayjs(endTime).endOf("month").format("YYYY-MM-DD");
|
|
609
|
-
});
|
|
610
|
-
break;
|
|
611
|
-
}
|
|
612
|
-
default: {
|
|
613
|
-
brushStartTime = time.find((item) => {
|
|
614
|
-
const condition1 = dayjs(item).format("YYYY-MM-DD") === dayjs(startTime).format("YYYY-MM-DD");
|
|
615
|
-
const condition2 = new Date(item).getTime() >= new Date(startTime).getTime();
|
|
616
|
-
return condition1 && condition2;
|
|
617
|
-
});
|
|
618
|
-
brushEndTime = time.findLast((item) => {
|
|
619
|
-
const condition1 = dayjs(item).format("YYYY-MM-DD") === dayjs(endTime).format("YYYY-MM-DD");
|
|
620
|
-
const condition2 = new Date(item).getTime() <= new Date(endTime).getTime();
|
|
621
|
-
return condition1 && condition2;
|
|
622
|
-
});
|
|
527
|
+
mainChartIns?.on("globalout", () => {
|
|
528
|
+
const timer = setTimeout(() => {
|
|
529
|
+
clearTimeout(timer);
|
|
530
|
+
const index = mainChartIns?.getOption()?.dataZoom?.[0]?.endValue;
|
|
531
|
+
activeIndex.value = index;
|
|
532
|
+
}, 30);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
let echartsContextMenuTimer = null;
|
|
536
|
+
mainChartIns?.on("contextmenu", (params) => {
|
|
537
|
+
echartsContextMenuTimer = setTimeout(() => {
|
|
538
|
+
if (params.componentType === "graphic") {
|
|
539
|
+
warningItem.value = params.info;
|
|
540
|
+
menuData.value = [
|
|
541
|
+
{ label: "删除画线", key: "deleteWarningLine" },
|
|
542
|
+
{ label: "修改画线", key: "changeWarningLine" },
|
|
543
|
+
];
|
|
623
544
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
type: "brush",
|
|
627
|
-
areas: [
|
|
628
|
-
{
|
|
629
|
-
brushType: "lineX",
|
|
630
|
-
coordRange: [brushStartTime, brushEndTime],
|
|
631
|
-
xAxisIndex: 0,
|
|
632
|
-
},
|
|
633
|
-
],
|
|
545
|
+
clearTimeout(echartsContextMenuTimer);
|
|
546
|
+
echartsContextMenuTimer = null;
|
|
634
547
|
});
|
|
635
|
-
}
|
|
548
|
+
});
|
|
636
549
|
};
|
|
637
550
|
|
|
638
|
-
//
|
|
551
|
+
// 预警线相关辅助函数
|
|
639
552
|
const updateWarningLineAndDraw = async () => {
|
|
640
553
|
const res = await getWarningLine({ featureId: props.featureId });
|
|
641
554
|
warningLineData.value = res?.body || [];
|
|
642
555
|
drawLine();
|
|
643
556
|
emit("change", "warningLine", warningLineData.value);
|
|
644
557
|
};
|
|
645
|
-
|
|
646
558
|
const menuClick = async (item) => {
|
|
647
559
|
const { variety, varietyName, featureId, featureType } = props;
|
|
560
|
+
|
|
648
561
|
if (item.key === "drawWarningLine") {
|
|
649
|
-
// 画线预警
|
|
650
|
-
// 拿到当前主图配置项
|
|
651
562
|
const mainChartOption = mainChartIns?.getOption();
|
|
652
563
|
const oldTooltip = mainChartOption.tooltip[0];
|
|
564
|
+
|
|
653
565
|
mainChartIns?.setOption({
|
|
654
566
|
...mainChartOption,
|
|
655
|
-
// 指示器样式
|
|
656
567
|
tooltip: {
|
|
657
568
|
...oldTooltip,
|
|
658
|
-
// 坐标轴指示器
|
|
659
569
|
axisPointer: {
|
|
660
|
-
// 保留原有部分配置
|
|
661
570
|
...oldTooltip.axisPointer,
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
width: 0,
|
|
665
|
-
},
|
|
666
|
-
// Y轴指示线的宽度
|
|
667
|
-
crossStyle: {
|
|
668
|
-
width: 2,
|
|
669
|
-
},
|
|
670
|
-
// 文本标签
|
|
571
|
+
lineStyle: { width: 0 },
|
|
572
|
+
crossStyle: { width: 2 },
|
|
671
573
|
label: {
|
|
672
|
-
// 保留原有部分配置
|
|
673
574
|
...oldTooltip.axisPointer.label,
|
|
674
|
-
// 颜色改为透明
|
|
675
575
|
backgroundColor: "transparent",
|
|
676
|
-
|
|
677
|
-
formatter: (data) => "",
|
|
576
|
+
formatter: () => "",
|
|
678
577
|
},
|
|
679
578
|
},
|
|
680
579
|
},
|
|
681
580
|
});
|
|
682
|
-
|
|
581
|
+
|
|
683
582
|
const handleClick = async (el) => {
|
|
684
|
-
// (1).在触发点击事件后,第一时间将图表绑定的点击事件清除
|
|
685
583
|
mainChartIns?.getZr().off("mousedown", handleClick);
|
|
686
|
-
// (2).根据点击位置获取到Y轴具体数据
|
|
687
584
|
const yAxisValue = mainChartIns?.convertFromPixel({ yAxisIndex: 0 }, el.offsetY);
|
|
688
|
-
// (3).格式化画线预警价格
|
|
689
585
|
const newWarnPrice = Math.round(yAxisValue * 1000) / 1000;
|
|
690
|
-
|
|
586
|
+
|
|
691
587
|
mainChartIns?.setOption({
|
|
692
588
|
...mainChartOption,
|
|
693
|
-
// 指示器样式
|
|
694
589
|
tooltip: {
|
|
695
590
|
...oldTooltip,
|
|
696
591
|
axisPointer: {
|
|
697
592
|
...oldTooltip.axisPointer,
|
|
698
|
-
lineStyle: {
|
|
699
|
-
|
|
700
|
-
},
|
|
701
|
-
crossStyle: {
|
|
702
|
-
width: 1,
|
|
703
|
-
},
|
|
593
|
+
lineStyle: { width: 1 },
|
|
594
|
+
crossStyle: { width: 1 },
|
|
704
595
|
label: {
|
|
705
596
|
...oldTooltip.axisPointer.label,
|
|
706
597
|
backgroundColor: null,
|
|
@@ -708,21 +599,20 @@ const menuClick = async (item) => {
|
|
|
708
599
|
},
|
|
709
600
|
},
|
|
710
601
|
});
|
|
711
|
-
|
|
602
|
+
|
|
712
603
|
await addWarningLine({
|
|
713
|
-
featureCode: variety,
|
|
714
|
-
featureName: varietyName,
|
|
715
|
-
featureId,
|
|
716
|
-
featureType,
|
|
604
|
+
featureCode: variety,
|
|
605
|
+
featureName: varietyName,
|
|
606
|
+
featureId,
|
|
607
|
+
featureType,
|
|
717
608
|
warnPrice: newWarnPrice,
|
|
718
609
|
});
|
|
719
610
|
ElMessage.success("画线预警成功!");
|
|
720
611
|
updateWarningLineAndDraw();
|
|
721
612
|
};
|
|
722
|
-
|
|
613
|
+
|
|
723
614
|
mainChartIns?.getZr().on("mousedown", handleClick);
|
|
724
615
|
} else if (item.key === "deleteWarningLine") {
|
|
725
|
-
// 删除预警线
|
|
726
616
|
await deleteWarningLine({ id: warningItem.value.id });
|
|
727
617
|
ElMessage.success("画线预警删除成功");
|
|
728
618
|
updateWarningLineAndDraw();
|
|
@@ -731,25 +621,55 @@ const menuClick = async (item) => {
|
|
|
731
621
|
warningLineChangeValue.value = warningItem.value.warnPrice;
|
|
732
622
|
}
|
|
733
623
|
};
|
|
734
|
-
|
|
735
624
|
const changeWarningLine = async () => {
|
|
736
625
|
const { variety, varietyName, featureId, featureType } = props;
|
|
737
626
|
await updateWarningLine({
|
|
738
627
|
id: warningItem.value.id,
|
|
739
|
-
featureCode: variety,
|
|
740
|
-
featureName: varietyName,
|
|
741
|
-
featureId,
|
|
742
|
-
featureType,
|
|
628
|
+
featureCode: variety,
|
|
629
|
+
featureName: varietyName,
|
|
630
|
+
featureId,
|
|
631
|
+
featureType,
|
|
743
632
|
warnPrice: warningLineChangeValue.value,
|
|
744
633
|
});
|
|
745
634
|
ElMessage.success("画线预警修改成功");
|
|
746
635
|
updateWarningLineAndDraw();
|
|
747
636
|
};
|
|
748
|
-
|
|
749
637
|
const closeContextMenuCallBack = () => {
|
|
750
638
|
menuData.value = [...defaultMenuData];
|
|
751
639
|
};
|
|
752
640
|
|
|
641
|
+
onMounted(() => {
|
|
642
|
+
initRequestByEnv(props.env);
|
|
643
|
+
getMainData();
|
|
644
|
+
window.addEventListener("keydown", handleKeyDownEvent);
|
|
645
|
+
});
|
|
646
|
+
watch(
|
|
647
|
+
() => [props.variety, props.cycle, props.mainIndicator, subIndicator.value, props.indicatorStore?.filterIndicator, props.indicatorStore?.customIndicator],
|
|
648
|
+
() => {
|
|
649
|
+
// 重置加载状态
|
|
650
|
+
isLoadHistory = false;
|
|
651
|
+
isloadAllHistory = false;
|
|
652
|
+
getMainData();
|
|
653
|
+
},
|
|
654
|
+
{ deep: true },
|
|
655
|
+
);
|
|
656
|
+
onUnmounted(() => {
|
|
657
|
+
// 取消所有动画帧
|
|
658
|
+
if (renderFrameId) {
|
|
659
|
+
cancelAnimationFrame(renderFrameId);
|
|
660
|
+
renderFrameId = null;
|
|
661
|
+
}
|
|
662
|
+
if (drawLineFrameId) {
|
|
663
|
+
cancelAnimationFrame(drawLineFrameId);
|
|
664
|
+
drawLineFrameId = null;
|
|
665
|
+
}
|
|
666
|
+
mainChartIns?.off("datazoom");
|
|
667
|
+
mainChartIns?.off("highlight");
|
|
668
|
+
mainChartIns?.off("globalout");
|
|
669
|
+
mainChartIns?.dispose();
|
|
670
|
+
resizeRo?.dispose();
|
|
671
|
+
window.removeEventListener("keydown", handleKeyDownEvent);
|
|
672
|
+
});
|
|
753
673
|
defineExpose({
|
|
754
674
|
draw: (type, data) => {
|
|
755
675
|
if (type === "warningLine") {
|
|
@@ -760,6 +680,107 @@ defineExpose({
|
|
|
760
680
|
});
|
|
761
681
|
</script>
|
|
762
682
|
|
|
683
|
+
<template>
|
|
684
|
+
<div
|
|
685
|
+
class="klineBasic"
|
|
686
|
+
@mousemove="isHover = true"
|
|
687
|
+
@mouseout="isHover = false"
|
|
688
|
+
>
|
|
689
|
+
<div class="klineBasic-tips">
|
|
690
|
+
<KlineTips
|
|
691
|
+
:variety="variety"
|
|
692
|
+
:data="chartData"
|
|
693
|
+
:activeIndex="activeIndex"
|
|
694
|
+
/>
|
|
695
|
+
</div>
|
|
696
|
+
<div
|
|
697
|
+
class="klineBasic-main"
|
|
698
|
+
:style="{ height: config.showSubChart ? '70%' : '100%' }"
|
|
699
|
+
>
|
|
700
|
+
<Contextmenu @closeContextMenuCallBack="closeContextMenuCallBack">
|
|
701
|
+
<div
|
|
702
|
+
ref="klineBasicMainRef"
|
|
703
|
+
style="height: 100%"
|
|
704
|
+
></div>
|
|
705
|
+
<template #popover>
|
|
706
|
+
<el-menu
|
|
707
|
+
:style="{
|
|
708
|
+
borderRadius: '4px',
|
|
709
|
+
overflow: 'hidden',
|
|
710
|
+
background: '#fff',
|
|
711
|
+
borderRight: 0,
|
|
712
|
+
}"
|
|
713
|
+
>
|
|
714
|
+
<el-menu-item
|
|
715
|
+
v-for="item in menuData"
|
|
716
|
+
style="height: 36px"
|
|
717
|
+
:key="item.key"
|
|
718
|
+
:index="item.key"
|
|
719
|
+
@click="menuClick(item)"
|
|
720
|
+
>
|
|
721
|
+
{{ item.label }}
|
|
722
|
+
</el-menu-item>
|
|
723
|
+
</el-menu>
|
|
724
|
+
</template>
|
|
725
|
+
</Contextmenu>
|
|
726
|
+
</div>
|
|
727
|
+
<div
|
|
728
|
+
class="klineBasic-sub"
|
|
729
|
+
v-if="config.showSubChart"
|
|
730
|
+
>
|
|
731
|
+
<KlineSub
|
|
732
|
+
ref="klineSubRef"
|
|
733
|
+
v-model="subIndicator"
|
|
734
|
+
:data="chartData"
|
|
735
|
+
:cycle="cycle"
|
|
736
|
+
:activeIndex="activeIndex"
|
|
737
|
+
:subIndicatorList="indicatorStore?.subIndicatorList"
|
|
738
|
+
/>
|
|
739
|
+
</div>
|
|
740
|
+
<div
|
|
741
|
+
class="klineBasic-empty"
|
|
742
|
+
v-if="isEmpty"
|
|
743
|
+
>
|
|
744
|
+
<el-empty
|
|
745
|
+
class="klineBasic-empty-content"
|
|
746
|
+
description="暂无数据"
|
|
747
|
+
/>
|
|
748
|
+
</div>
|
|
749
|
+
<div
|
|
750
|
+
class="klineBasic-error"
|
|
751
|
+
v-if="isError"
|
|
752
|
+
>
|
|
753
|
+
<div class="klineBasic-error-content">加载失败,请刷新重试</div>
|
|
754
|
+
<div style="text-align: center">
|
|
755
|
+
<el-button @click="getMainData">刷新</el-button>
|
|
756
|
+
</div>
|
|
757
|
+
</div>
|
|
758
|
+
</div>
|
|
759
|
+
<!-- 画线预警-修改价格弹窗 -->
|
|
760
|
+
<el-dialog
|
|
761
|
+
v-model="warningLineChangeVisible"
|
|
762
|
+
title="画线预警-修改价格"
|
|
763
|
+
width="30%"
|
|
764
|
+
align-center
|
|
765
|
+
>
|
|
766
|
+
<span style="margin-right: 10px">预警价格:</span>
|
|
767
|
+
<el-input-number
|
|
768
|
+
v-model="warningLineChangeValue"
|
|
769
|
+
placeholder="输入预警价格"
|
|
770
|
+
/>
|
|
771
|
+
<template #footer>
|
|
772
|
+
<span class="dialog-footer">
|
|
773
|
+
<el-button @click="warningLineChangeVisible = false">取消</el-button>
|
|
774
|
+
<el-button
|
|
775
|
+
type="primary"
|
|
776
|
+
@click="changeWarningLine"
|
|
777
|
+
>确定</el-button
|
|
778
|
+
>
|
|
779
|
+
</span>
|
|
780
|
+
</template>
|
|
781
|
+
</el-dialog>
|
|
782
|
+
</template>
|
|
783
|
+
|
|
763
784
|
<style lang="scss" scoped>
|
|
764
785
|
.klineBasic {
|
|
765
786
|
width: 100%;
|