st-comp 0.0.45 → 0.0.47

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,304 @@
1
+ <template></template>
2
+
3
+ <script setup lang="ts">
4
+ import * as talib from 'talib.js'
5
+
6
+ let isInit = false // 是否初始化
7
+ let loading = true // 初始化loading
8
+ let requestPromise: any[] = [] // 用于存储loading中的计算请求
9
+
10
+ // 导出数据长度处理
11
+ const sliceData = (data: any[], length: number) => {
12
+ return data.length > length ? data.slice(data.length - length) : data
13
+ }
14
+
15
+ defineExpose({
16
+ // 注册计算指标线方法
17
+ initTalib: async () => {
18
+ if (!isInit) {
19
+ // 未初始化
20
+ isInit = true
21
+ await talib.init('./talib.wasm')
22
+ loading = false
23
+ requestPromise.forEach(resolve => {
24
+ resolve()
25
+ })
26
+ } else if(loading) {
27
+ // 正在初始化
28
+ return new Promise((resolve) => {
29
+ requestPromise.push(resolve)
30
+ })
31
+ }
32
+ },
33
+ // 键盘事件处理
34
+ handleKeyDown: (e: any, params: any) => {
35
+ let { startValue, endValue, maxIndex } = params
36
+ if (e.code === 'ArrowLeft') {
37
+ // 左按键
38
+ if (startValue === 0) {
39
+ return
40
+ }
41
+ startValue = startValue - 1
42
+ endValue = endValue - 1
43
+ } else if (e.code === 'ArrowRight') {
44
+ // 右按键
45
+ if (endValue === maxIndex) {
46
+ return
47
+ }
48
+ startValue = startValue + 1
49
+ endValue = endValue + 1
50
+ } else if (e.code === 'ArrowUp') {
51
+ // 上按键-放大 最少保持5条数据
52
+ if(endValue - startValue < 5) {
53
+ return
54
+ }
55
+ const diff = Math.floor((endValue - startValue) / 2) + 1
56
+ startValue = startValue + diff
57
+ if (endValue - startValue < 5) {
58
+ startValue = endValue - 4
59
+ }
60
+ } else if (e.code === 'ArrowDown') {
61
+ // 下按键-缩小
62
+ const diff = Math.min(500, (endValue - startValue))
63
+ startValue = startValue - diff - 1
64
+ }
65
+ return({
66
+ startValue,
67
+ endValue
68
+ })
69
+ },
70
+ // 根据时间解析dataZoom
71
+ getDataZoomInfoByTime: (params: any) => {
72
+ const { showStartTime, showEndTime, xAxisData, maxShowDays } = params
73
+ let startValue = -1
74
+ let endValue = -1
75
+ let dayPerTime: any = {}
76
+ xAxisData.forEach((time: any, index: number) => {
77
+ const date = time.split(' ')[0]
78
+ dayPerTime[date] = dayPerTime[date] || 0
79
+ dayPerTime[date] += 1
80
+ if (new Date(time) >= new Date(showStartTime) && startValue === -1) {
81
+ startValue = index
82
+ }
83
+ if (new Date(time) <= new Date(showEndTime)) {
84
+ endValue = index
85
+ }
86
+ })
87
+ return {
88
+ startValue,
89
+ endValue,
90
+ maxValueSpan: Math.max(...Object.values(dayPerTime)) * maxShowDays,
91
+ }
92
+ },
93
+ // 计算绘图数据(拖动轴使用)
94
+ formatDataBySlide: (formatDataParams: any) => {
95
+ const { originDrawData, addData, startTime, endTime, mainIndicator, mainIndicatorList, subIndicator, subIndicatorList, config, timeRange } = formatDataParams
96
+ let drawData: any = {}
97
+ const { loadAddCount, preLoadCount } = config
98
+
99
+ const time: any[] = [] // 新增数据全部时间数据,用于计算指标线
100
+ const open: any[] = [] // 新增数据全部开盘价,用于指标线计算
101
+ const close: any[] = [] // 新增数据全部收盘价,用于指标线计算
102
+ const high: any[] = [] // 新增数据全部最高价,用于指标线计算
103
+ const low: any[] = [] // 新增数据全部最低价,用于指标线计算
104
+ let xAxisData: any[] = [] // 用于绘图的x轴时间数据
105
+ let candlestickData: any[] = [] // 用于绘图的k线图数据
106
+
107
+ const originData = addData.filter((item: any, index: number) => {
108
+ // 赋值
109
+ time.push(item[0])
110
+ open.push(item[1])
111
+ close.push(item[4])
112
+ high.push(item[2])
113
+ low.push(item[3])
114
+ xAxisData.push(item[0])
115
+ candlestickData.push([ item[1], item[4], item[3], item[2], index === 0 ? 0 : addData[index - 1][4] ])
116
+ // 取范围中的数据
117
+ if (startTime && endTime) {
118
+ return new Date(startTime) <= new Date(item[0]) && new Date(item[0]) <= new Date(endTime)
119
+ } else if (endTime) {
120
+ return index >= addData.length - loadAddCount && new Date(timeRange[0]) <= new Date(item[0]) && new Date(item[0]) <= new Date(timeRange[1])
121
+ } else if (startTime) {
122
+ return index >= preLoadCount && new Date(timeRange[0]) <= new Date(item[0]) && new Date(item[0]) <= new Date(timeRange[1])
123
+ }
124
+ return false
125
+ })
126
+ xAxisData = sliceData(xAxisData, originData.length)
127
+ candlestickData = sliceData(candlestickData, originData.length)
128
+ // 指标线相关计算
129
+ const indicatorFormatData = { time, open, close, high, low, originData: addData }
130
+ const mainIndicatorConfig = mainIndicatorList.find((item: any) => item.value === mainIndicator).config
131
+ const mainIndicatorData = mainIndicatorConfig.map((item: any) => ({
132
+ ...item,
133
+ data: sliceData(item.calculationFn ? item.calculationFn(talib, indicatorFormatData, {
134
+ time: [], open: [], close: [], high: [], low: [], originData: []
135
+ }) : [], xAxisData.length),
136
+ }))
137
+ const subIndicatorConfig = subIndicatorList.find((item: any) => item.value === subIndicator).config
138
+ const subIndicatorData = subIndicatorConfig.reduce((res: any[], item: any) => {
139
+ if (item.source === 'calculation') {
140
+ return [
141
+ ...res,
142
+ {
143
+ ...item,
144
+ data: sliceData(item.calculationFn ? item.calculationFn(talib, indicatorFormatData) : [], xAxisData.length)
145
+ }
146
+ ]
147
+ }
148
+ return res
149
+ }, [])
150
+ // 根据不同情况处理数据
151
+ if (startTime && endTime) {
152
+ // 重新搜索
153
+ drawData = {
154
+ originData, // 原数据
155
+ xAxisData, // 时间数据
156
+ candlestickData, // k线数据
157
+ mainIndicatorData, // 主图指标线数据
158
+ subIndicatorData, // 副图数据
159
+ }
160
+ } else if (endTime) {
161
+ // 加载历史数据
162
+ drawData = {
163
+ originData: [...originData, ...originDrawData.originData], // 原数据
164
+ xAxisData: [...xAxisData, ...originDrawData.xAxisData], // 时间数据
165
+ candlestickData: [...candlestickData, ...originDrawData.candlestickData], // k线数据
166
+ mainIndicatorData: originDrawData.mainIndicatorData.map((item: any, index: number) => {
167
+ return {
168
+ ...item,
169
+ data: [...mainIndicatorData[index].data, ...item.data],
170
+ }
171
+ }), // 主图指标线数据
172
+ subIndicatorData: originDrawData.subIndicatorData.map((item: any, index: number) => {
173
+ return {
174
+ ...item,
175
+ data: [...subIndicatorData[index].data, ...item.data],
176
+ }
177
+ }), // 副图数据
178
+ }
179
+ } else if (startTime) {
180
+ // 加载新数据
181
+ drawData = {
182
+ originData: [...originDrawData.originData, ...originData], // 原数据
183
+ xAxisData: [...originDrawData.xAxisData, ...xAxisData], // 时间数据
184
+ candlestickData: [...originDrawData.candlestickData, ...candlestickData], // k线数据
185
+ mainIndicatorData: originDrawData.mainIndicatorData.map((item: any, index: number) => {
186
+ return {
187
+ ...item,
188
+ data: [...item.data, ...mainIndicatorData[index].data],
189
+ }
190
+ }), // 主图指标线数据
191
+ subIndicatorData: originDrawData.subIndicatorData.map((item: any, index: number) => {
192
+ return {
193
+ ...item,
194
+ data: [...item.data, ...subIndicatorData[index].data],
195
+ }
196
+ }), // 副图数据
197
+ }
198
+ }
199
+ // 添加一些字段,用于方便使用
200
+ drawData.length = drawData.xAxisData.length
201
+ drawData.startTime = drawData.xAxisData[0]
202
+ drawData.endTime = drawData.xAxisData[drawData.length - 1]
203
+ return drawData
204
+ },
205
+ // 计算绘图数据(根据长度使用)
206
+ formatDataByCount: (formatDataParams: any) => {
207
+ const { originDrawData, addData, mainIndicator, mainIndicatorList, subIndicator, subIndicatorList, config } = formatDataParams
208
+ let drawData: any = {}
209
+ const { addCount, preLoadCount } = config
210
+
211
+ const time: any[] = [] // 新增数据全部时间数据,用于计算指标线
212
+ const open: any[] = [] // 新增数据全部开盘价,用于指标线计算
213
+ const close: any[] = [] // 新增数据全部收盘价,用于指标线计算
214
+ const high: any[] = [] // 新增数据全部最高价,用于指标线计算
215
+ const low: any[] = [] // 新增数据全部最低价,用于指标线计算
216
+ let xAxisData: any[] = [] // 用于绘图的x轴时间数据
217
+ let candlestickData: any[] = [] // 用于绘图的k线图数据
218
+
219
+ const originData = addData.filter((item: any, index: number) => {
220
+ // 赋值
221
+ time.push(item[0])
222
+ open.push(item[1])
223
+ close.push(item[4])
224
+ high.push(item[2])
225
+ low.push(item[3])
226
+ xAxisData.push(item[0])
227
+ candlestickData.push([ item[1], item[4], item[3], item[2], index === 0 ? 0 : addData[index - 1][4] ])
228
+ if (addData.length >= addCount + preLoadCount - 1) {
229
+ // 还有未加载数据
230
+ return index >= preLoadCount
231
+ } else {
232
+ // 全部剩余数据
233
+ return true
234
+ }
235
+ })
236
+ xAxisData = sliceData(xAxisData, originData.length)
237
+ candlestickData = sliceData(candlestickData, originData.length)
238
+ // 指标线相关计算
239
+ const indicatorFormatData = { time, open, close, high, low, originData: addData }
240
+ const mainIndicatorConfig = mainIndicatorList.find((item: any) => item.value === mainIndicator).config
241
+ const mainIndicatorData = mainIndicatorConfig.map((item: any) => ({
242
+ ...item,
243
+ data: sliceData(item.calculationFn ? item.calculationFn(talib, indicatorFormatData, {
244
+ time: [], open: [], close: [], high: [], low: [], originData: []
245
+ }) : [], xAxisData.length),
246
+ }))
247
+ let subIndicatorData = []
248
+ if (subIndicator && subIndicatorList) {
249
+ const subIndicatorConfig = subIndicatorList.find((item: any) => item.value === subIndicator).config
250
+ subIndicatorData = subIndicatorConfig.reduce((res: any[], item: any) => {
251
+ if (item.source === 'calculation') {
252
+ return [
253
+ ...res,
254
+ {
255
+ ...item,
256
+ data: sliceData(item.calculationFn ? item.calculationFn(talib, indicatorFormatData) : [], xAxisData.length)
257
+ }
258
+ ]
259
+ }
260
+ return res
261
+ }, [])
262
+ }
263
+ // 根据不同情况处理数据
264
+ if (originDrawData.length) {
265
+ // 加载历史数据
266
+ drawData = {
267
+ originData: [...originData, ...originDrawData.originData], // 原数据
268
+ xAxisData: [...xAxisData, ...originDrawData.xAxisData], // 时间数据
269
+ candlestickData: [...candlestickData, ...originDrawData.candlestickData], // k线数据
270
+ mainIndicatorData: originDrawData.mainIndicatorData.map((item: any, index: number) => {
271
+ return {
272
+ ...item,
273
+ data: [...mainIndicatorData[index].data, ...item.data],
274
+ }
275
+ }), // 主图指标线数据
276
+ subIndicatorData: originDrawData.subIndicatorData.map((item: any, index: number) => {
277
+ return {
278
+ ...item,
279
+ data: [...subIndicatorData[index].data, ...item.data],
280
+ }
281
+ }), // 副图数据
282
+ }
283
+ } else {
284
+ // 首屏数据
285
+ drawData = {
286
+ originData, // 原数据
287
+ xAxisData, // 时间数据
288
+ candlestickData, // k线数据
289
+ mainIndicatorData, // 主图指标线数据
290
+ subIndicatorData, // 副图数据
291
+ }
292
+ }
293
+ // 添加一些字段,用于方便使用
294
+ drawData.length = drawData.xAxisData.length
295
+ drawData.startTime = drawData.xAxisData[0]
296
+ drawData.endTime = drawData.xAxisData[drawData.length - 1]
297
+ return drawData
298
+ },
299
+ fn: () => {},
300
+ })
301
+ </script>
302
+
303
+ <style lang="scss" scoped>
304
+ </style>
@@ -1,8 +1,14 @@
1
1
  import { App } from "vue";
2
- import StKlineNew from "./index.vue";
2
+ import KlineUtils from "./components/KlineUtils/index.vue"
3
+ import KlineTips from "./components/KlineTips/index.vue"
4
+ import KlineSub from "./components/KlineSub/index.vue"
5
+ import KlineSlide from "./components/KlineSlide/index.vue"
3
6
 
4
7
  export default {
5
8
  install(app: App) {
6
- app.component("st-klineNew", StKlineNew);
9
+ app.component("st-klineUtils", KlineUtils);
10
+ app.component("st-klineSub", KlineSub);
11
+ app.component("st-klineTips", KlineTips);
12
+ app.component("st-klineSlide", KlineSlide);
7
13
  },
8
14
  }
@@ -0,0 +1,91 @@
1
+ <!-- 提供右键菜单的父级DOM组件 -->
2
+ <template>
3
+ <div ref="contextmenuRef" class="contextmenu" @contextmenu="handleContextMenu" @mouseleave="handleMouseLeave">
4
+ <!-- 主DOM插槽 -->
5
+ <slot />
6
+ <!-- 自定义菜单插槽 -->
7
+ <div ref="menuRef" v-if="menuStyle.display !== 'none'" class="contextmenu-popover" :style="menuStyle">
8
+ <slot name="popover" />
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <script setup>
14
+ import { ref, nextTick } from "vue";
15
+ // 自定义事件函数: 关闭悬浮菜单回调
16
+ const emits = defineEmits(["closeMenuCallBack"]);
17
+ // 组件Ref
18
+ const contextmenuRef = ref();
19
+ // 悬浮菜单Ref
20
+ const menuRef = ref();
21
+ // 悬浮菜单样式
22
+ const menuStyle = ref({
23
+ display: "none", // 是否展示
24
+ top: "0px", // 位置:上边距
25
+ left: "0px", // 位置:左边距
26
+ });
27
+
28
+ // 打开悬浮菜单
29
+ const handleContextMenu = (el) => {
30
+ // 1.阻止默认事件 + 冒泡
31
+ el.preventDefault();
32
+ el.stopPropagation();
33
+ // @todo: 解决: 右键菜单项,重复触发本函数导致菜单位置异常问题
34
+ if (menuStyle.value.display === "block") {
35
+ // 判断鼠标位置是否在悬浮菜单内,如果是,则return,否则继续执行在新的位置打开
36
+ const { offsetX, offsetY } = el; // 鼠标
37
+ const { offsetWidth, offsetHeight } = menuRef.value;
38
+ if (offsetX <= offsetWidth && offsetY <= offsetHeight) {
39
+ return;
40
+ }
41
+ }
42
+ contextmenuRef.value.click();
43
+ // 2.初始化展示悬浮菜单位置
44
+ menuStyle.value = {
45
+ display: "block",
46
+ top: `${el.offsetY}px`,
47
+ left: `${el.offsetX}px`,
48
+ };
49
+ // 3.计算悬浮菜单是否超出范围
50
+ nextTick(() => {
51
+ const { bottom, height, right, width } = menuRef.value.getBoundingClientRect();
52
+ if (bottom > window.innerHeight) {
53
+ // 高度超出
54
+ menuStyle.value = {
55
+ display: "block",
56
+ top: `${el.offsetY - height}px`,
57
+ left: `${el.offsetX}px`,
58
+ };
59
+ } else if (right > window.innerWidth) {
60
+ // 宽度超出
61
+ menuStyle.value = {
62
+ display: "block",
63
+ top: `${el.offsetY}px`,
64
+ left: `${el.offsetX - width}px`,
65
+ };
66
+ }
67
+ });
68
+ // 4.监听点击事件 -> 调用关闭悬浮菜单事件
69
+ document.addEventListener("click", handleMouseLeave);
70
+ };
71
+
72
+ // 关闭悬浮菜单
73
+ const handleMouseLeave = () => {
74
+ menuStyle.value.display = "none";
75
+ emits("closeMenuCallBack");
76
+ document.removeEventListener("click", handleMouseLeave);
77
+ };
78
+ </script>
79
+
80
+ <style lang="scss" scoped>
81
+ .contextmenu {
82
+ position: relative;
83
+ width: 100%;
84
+ height: 100%;
85
+ &-popover {
86
+ position: absolute;
87
+ z-index: 10;
88
+ display: none;
89
+ }
90
+ }
91
+ </style>