st-comp 0.0.13 → 0.0.16

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.
Files changed (37) hide show
  1. package/auto-imports.d.ts +1 -1
  2. package/components.d.ts +2 -1
  3. package/lib/bundle.js +10145 -9151
  4. package/lib/bundle.umd.cjs +10 -12
  5. package/lib/style.css +1 -1
  6. package/lib/talib.wasm +0 -0
  7. package/package.json +4 -1
  8. package/packages/Kline/components/Contextmenu/index.vue +110 -0
  9. package/packages/Kline/components/Tips/index.vue +18 -86
  10. package/packages/Kline/formatKlineData.ts +67 -40
  11. package/packages/Kline/images/buy.svg +1 -0
  12. package/packages/Kline/images/pen.png +0 -0
  13. package/packages/Kline/images/sell.svg +1 -0
  14. package/packages/Kline/images/t.svg +1 -0
  15. package/packages/Kline/index.vue +439 -119
  16. package/packages/Kline/option.ts +316 -0
  17. package/packages/Kline/type.d.ts +192 -24
  18. package/packages/Kline/utils.ts +576 -171
  19. package/packages/Table/components/Button/index.vue +7 -0
  20. package/packages/Table/index.vue +37 -24
  21. package/packages/index.ts +0 -2
  22. package/public/talib.wasm +0 -0
  23. package/src/pages/ChartLayout/index.vue +22 -22
  24. package/src/pages/Kline/api.ts +57 -0
  25. package/src/pages/Kline/components/MultiCycleSingleVariety.vue +728 -0
  26. package/src/pages/Kline/components/SingleCycleSingleVariety.vue +663 -0
  27. package/src/pages/Kline/index.vue +85 -16
  28. package/src/router/routes.ts +0 -5
  29. package/src/style.css +75 -0
  30. package/vite.config.ts +37 -27
  31. package/vitePlugins/testRelese.ts +67 -0
  32. package/packages/Echarts/index.ts +0 -8
  33. package/packages/Echarts/index.vue +0 -113
  34. package/packages/Kline/kline_theme_dark.json +0 -30
  35. package/packages/Kline/kline_theme_light.json +0 -30
  36. package/src/components/Echarts/index.vue +0 -31
  37. package/src/pages/Echarts/index.vue +0 -12
@@ -1,141 +1,308 @@
1
- <template>
2
- <div class="st-kline">
3
- <!-- 顶部Tips -->
4
- <div class="st-kline-header">
5
- <Tips
6
- :activeIndex="activeIndex"
7
- :klineData="klineData"
8
- :indicatorData="indicatorData"
9
- :indicatorConfig="indicatorConfigList[indicator]"
10
- />
11
- </div>
12
- <!-- 图表 -->
13
- <st-echarts
14
- v-if="option"
15
- :option="option"
16
- :themeConfig="{
17
- name: 'kline',
18
- light: klineThemeLight,
19
- dark: klineThemeDark,
20
- }"
21
- @highlight="highlight"
22
- @afterInit="afterInit"
23
- />
24
- </div>
25
- </template>
26
-
27
1
  <script setup lang="ts">
28
- import dayjs from 'dayjs'
29
- import { ref, onMounted } from 'vue'
2
+ import * as echarts from 'echarts'
3
+ import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
30
4
  import Tips from './components/Tips/index.vue'
31
- import klineThemeDark from './kline_theme_dark.json'
32
- import klineThemeLight from './kline_theme_light.json'
33
- import type { EChartsType, EChartsOption } from 'echarts'
34
- import type { KlineDataType, IndicatorDataType, IndicatorConfigType } from './type.d.ts'
35
- import { getKline, getIndicator, getIndicatorConfigList, getOption } from './utils'
5
+ import Contextmenu from './components/Contextmenu/index.vue'
6
+ import { getOption, getLineOption } from './option'
7
+ import { printConsole, formatPrice } from './utils'
8
+ import type { EChartsType, EChartsOption, ECElementEvent, DataZoomComponentOption } from 'echarts'
9
+ import type { KlineDataType, LineDataType, InConfig, MenuDataType } from './type.d.ts'
36
10
 
37
11
  /**
38
12
  * 组件接收参数
39
- * @param {String} code 品种代码
40
- * @param {Number} defaultCount 默认K线总条数
41
- * @param {String} tradeDate 最终交易日时间
42
- * @param {String} frequency 周期名称
43
- * @param {String} indicator 指标线类型
13
+ * @param {Object} indicator 指标线
14
+ * @param {KlineDataType} klineData K线数据
15
+ * @param {Array} markData 标注点位数据 [开平点, 买卖点, 信号点,...]
16
+ * @param {Array} lineData K线额外画线数据 [预警线, 持仓线, 条件单, ...]
17
+ * @param {Array} brushRange 区域刷选范围数据
18
+ * @param {Array} defaultMenuData 菜单项数据
19
+ *
20
+ * @param {InConfig} config 其他配置项数据
44
21
  */
45
22
  const props = defineProps({
46
- code: {
47
- type: String,
48
- required: true,
23
+ indicator: {
24
+ type: Object,
25
+ default: () => ({
26
+ DKX: 'LINE#FFFFFF',
27
+ EMA120: 'LINE#E4007F',
28
+ EMA240: 'LINE#999999',
29
+ EMA60: 'LINE#00FF00',
30
+ MADKX: 'LINE#FFFF00',
31
+ WINDOW: '#0',
32
+ }),
49
33
  },
50
- defaultCount: {
51
- type: Number,
52
- default: () => 500,
34
+ klineData: {
35
+ type: Array,
36
+ default: () => [],
53
37
  },
54
- tradeDate: {
55
- type: String,
56
- required: true,
38
+ markData: {
39
+ type: Array,
40
+ default: () => [],
57
41
  },
58
- frequency: {
59
- type: String,
60
- required: true,
42
+ lineData: {
43
+ type: Array,
44
+ default: () => [],
61
45
  },
62
- indicator: {
63
- type: String,
64
- required: true,
46
+ brushRange: {
47
+ type: Array,
48
+ default: () => [],
49
+ },
50
+ defaultMenuData: {
51
+ type: Array,
52
+ default: () => [],
53
+ },
54
+ config: {
55
+ type: Object,
56
+ default: () => ({}),
65
57
  },
66
58
  })
67
59
 
68
- const option = ref<any>(null) // 图表配置
69
- const klineData = ref<KlineDataType>([]) // K线数据
70
- const indicatorData = ref<IndicatorDataType>([]) // 指标线具体数据
71
- const indicatorConfigList = ref<IndicatorConfigType>({}) // 指标线配置项
72
- const activeIndex = ref(0) // 当前鼠标激活的数据索引
73
-
74
- // 获取K线数据
75
- const getKlineData = async () => {
76
- const { code, defaultCount, tradeDate, frequency } = props
77
- const res = await getKline(code, defaultCount, tradeDate, frequency)
78
- return res.data
60
+ /**
61
+ * K线功能默认配置
62
+ * @param {Number} totalBarCount k线总条数
63
+ * @param {Number} defaultShowBarCount k线默认展示条数
64
+ * @param {Number} preBarCount k线预加载条数,用于计算指标线
65
+ * ---------------------------------------------------------
66
+ * @param {Number} gridLeft k线组件grid: left
67
+ * @param {Number} gridTop k线组件grid: top
68
+ * @param {Number} gridRight k线组件grid: right
69
+ * @param {Number} gridBottom k线组件grid: bottom
70
+ * ---------------------------------------------------------
71
+ * @param {Object} warningConfig 预警线配置
72
+ * @param {Object} positionConfig 持仓线配置
73
+ * @param {Object} conditionConfig 条件单配置
74
+ */
75
+ const defaultConfig: InConfig = {
76
+ totalBarCount: 3000,
77
+ defaultShowBarCount: 120,
78
+ preBarCount: 800,
79
+ gridLeft: 60,
80
+ gridTop: 0,
81
+ gridRight: 60,
82
+ gridBottom: 30,
83
+ // 预警线配置
84
+ warningConfig: {
85
+ draggable: false,
86
+ lineColor: '#fff',
87
+ textColor: '#fff',
88
+ },
89
+ // 持仓线配置
90
+ positionConfig: {
91
+ draggable: false,
92
+ lineColor: '#e45d07',
93
+ textColor: '#fff',
94
+ },
95
+ // 条件单配置
96
+ conditionConfig: {
97
+ draggable: false,
98
+ lineColor: '#fff',
99
+ textColor: '#fff',
100
+ profitLineColor: '#b71e44',
101
+ profitTextColor: '#fff',
102
+ lossLineColor: '#749b66',
103
+ lossTextColor: '#fff',
104
+ },
79
105
  }
80
106
 
81
- // 获取指标线具体数据
82
- const getIndicatorData = async (klineData: KlineDataType) => {
83
- if (klineData.length === 0) return
84
- const { code, frequency, indicator } = props
85
- const start_date = dayjs(new Date(klineData[0][0] * 1000)).format('YYYY-MM-DD')
86
- const end_date = dayjs(new Date(klineData[klineData.length - 1][0] * 1000)).format('YYYY-MM-DD')
87
- const indicatorRes = await getIndicator(code, start_date, end_date, frequency, indicator)
88
- // 格式处理,只格式化获取处于K线之中的指标线数据
89
- return indicatorRes.data.datetime
90
- ? indicatorRes.data.datetime.reduce((res: IndicatorDataType, date: number, index: number) => {
91
- if (date >= klineData[0][0] && date <= klineData[klineData.length - 1][0]) {
92
- return [
93
- ...res,
94
- {
95
- ...Object.keys(indicatorRes.data).reduce((keyRes, key) => {
96
- return {
97
- ...keyRes,
98
- [key]: indicatorRes.data[key][index],
99
- }
100
- }, {}),
101
- },
102
- ]
107
+ /**
108
+ * @description: 合并后的功能配置项
109
+ */
110
+ const config: { value: InConfig } = computed(() => {
111
+ return { ...defaultConfig, ...props.config }
112
+ })
113
+
114
+ const option = ref<any>(null) // 处理后的echarts配置
115
+ const activeIndex = ref(-1) // 当前鼠标激活的数据索引
116
+
117
+ // Tips: 开, 高, 低,
118
+ const kLineTips = computed(() => {
119
+ if (option.value && option.value.dataset[0]?.source?.klineData[activeIndex.value]) {
120
+ const klineItem = option.value.dataset[0].source.klineData[activeIndex.value]
121
+ return [
122
+ {
123
+ label: '开',
124
+ value: klineItem[1],
125
+ color: 'rgb(153, 153, 153)',
126
+ },
127
+ {
128
+ label: '高',
129
+ value: klineItem[2],
130
+ color: 'rgb(153, 153, 153)',
131
+ },
132
+ {
133
+ label: '低',
134
+ value: klineItem[3],
135
+ color: 'rgb(153, 153, 153)',
136
+ },
137
+ {
138
+ label: '收',
139
+ value: klineItem[4],
140
+ color: 'rgb(153, 153, 153)',
141
+ },
142
+ ]
143
+ }
144
+ return []
145
+ })
146
+ // Tips: 指标
147
+ const indicatorTips = computed(() => {
148
+ if (option.value && option.value.dataset[0]?.source?.indicatorData) {
149
+ return option.value.dataset[0].source.indicatorData.reduce((result: any, next: any) => {
150
+ next.data[activeIndex.value] &&
151
+ result.push({
152
+ label: next.key,
153
+ value: formatPrice(next.data[activeIndex.value]),
154
+ color: next.color,
155
+ })
156
+ return result
157
+ }, [])
158
+ }
159
+ return []
160
+ })
161
+
162
+ // 监视: props[klineData, indicator, markData] -> 重绘全部
163
+ watch(
164
+ () => [props.klineData, props.indicator, props.markData],
165
+ () => {
166
+ draw()
167
+ },
168
+ {
169
+ deep: true,
170
+ }
171
+ )
172
+ // 监视: props.lineData - 额外画线数据 -> 重新绘制额外线
173
+ watch(
174
+ () => props.lineData,
175
+ async () => {
176
+ printConsole('st-kline组件消息:props.lineData监控,额外画线数据发生改变,重绘->line', {
177
+ color: 'red',
178
+ })
179
+ draw('line')
180
+ },
181
+ {
182
+ deep: true,
183
+ }
184
+ )
185
+ // 监视: props.brushRange - 区域刷选数据 -> 重新刷选
186
+ watch(
187
+ () => props.brushRange,
188
+ async () => {
189
+ printConsole('st-kline组件消息:props.brushRange监控,区域刷选数据发生改变,重绘->brush', {
190
+ color: 'red',
191
+ })
192
+ draw('brush')
193
+ },
194
+ {
195
+ deep: true,
196
+ }
197
+ )
198
+
199
+ /**
200
+ * @description: 图表绘制函数
201
+ * @param {?String} type 绘制类型[Kline-K线,line-额外线条,brush-区域刷选,不传-全部]
202
+ */
203
+ const draw = async (type?: string) => {
204
+ const callBackMap = new Map([
205
+ // kline-K线绘制
206
+ [
207
+ 'kline',
208
+ async () => {
209
+ const newOption = await getOption(
210
+ props.klineData as KlineDataType,
211
+ props.markData,
212
+ props.indicator,
213
+ config.value
214
+ )
215
+ const { graphic } = echartsInstance.getOption() ?? { graphic: [] }
216
+ option.value = { ...newOption, graphic }
217
+ echartsInstance.setOption(option.value, true)
218
+ printConsole('st-kline组件消息:K线绘制完毕-draw', { color: 'green' })
219
+ },
220
+ ],
221
+ // line-额外线条绘制
222
+ [
223
+ 'line',
224
+ () => {
225
+ const echartsOptions = echartsInstance.getOption()
226
+ const graphic = getLineOption(props.lineData as LineDataType, config.value, echartsInstance)
227
+ option.value = { ...echartsOptions, graphic }
228
+ echartsInstance.setOption(option.value, {
229
+ replaceMerge: ['graphic'],
230
+ })
231
+ printConsole('st-kline组件消息:额外画线绘制完毕-draw', { color: 'green' })
232
+ },
233
+ ],
234
+ // brush-区域刷选
235
+ [
236
+ 'brush',
237
+ () => {
238
+ const { brushRange } = props
239
+ if (brushRange instanceof Array && brushRange.length > 0) {
240
+ echartsInstance.dispatchAction({
241
+ type: 'brush',
242
+ areas: [
243
+ {
244
+ brushType: 'lineX',
245
+ coordRange: [brushRange[0] + '', brushRange[1] + ''],
246
+ xAxisIndex: 0,
247
+ },
248
+ ],
249
+ })
250
+ printConsole('st-kline组件消息:区域刷选绘制完毕-draw', { color: 'green' })
103
251
  }
104
- return res
105
- }, [])
106
- : null
252
+ },
253
+ ],
254
+ // 全部绘制
255
+ [
256
+ undefined,
257
+ async () => {
258
+ await draw('kline')
259
+ await draw('line')
260
+ await draw('brush')
261
+ },
262
+ ],
263
+ ])
264
+ const callBack = callBackMap.get(type)
265
+ if (callBack instanceof Function) {
266
+ await callBack()
267
+ }
107
268
  }
108
269
 
109
- // 初始化
110
- const init = async () => {
111
- // 1.获取K线数据
112
- klineData.value = await getKlineData()
113
- // 2.默认Tips激活展示最后一条
114
- activeIndex.value = klineData.value.length - 1
115
- // 3.获取指标线具体数据
116
- indicatorData.value = await getIndicatorData(klineData.value)
117
- // 4.获取指标相关配置
118
- indicatorConfigList.value = (await getIndicatorConfigList()).data
119
- // 5.获取图表配置
120
- const params = {
121
- kLineData: klineData.value,
122
- indicatorData: indicatorData.value,
123
- indicator: props.indicator,
124
- indicatorConfigList: indicatorConfigList.value,
125
- defaultShowBarCount: 200,
126
- }
127
- option.value = await getOption(params)
270
+ //----------------------------右键菜单功能相关----------------------------------
271
+ const cursorPenVisible = ref(false) // 画笔模式开关
272
+
273
+ const menuData = ref<MenuDataType>([])
274
+
275
+ /**
276
+ * @description: 点击菜单项
277
+ * @param {Object} item 菜单项的数据
278
+ */
279
+ const menuClick = (item: any) => {
280
+ const { callBack } = item
281
+ callBack instanceof Function && callBack(echartsInstance, cursorPenVisible)
282
+ }
283
+
284
+ /**
285
+ * @description: 菜单关闭的回调
286
+ * @todo: 进行菜单内容的初始化
287
+ */
288
+ const closeContextMenuCallBack = () => {
289
+ menuData.value = props.defaultMenuData as MenuDataType
128
290
  }
129
291
 
292
+ //----------------------------Echarts基座-------------------------------
293
+ const echartsRef = ref<HTMLElement>()
294
+ let echartsInstance: EChartsType // echarts实例
295
+ let chartDomObserver: any // 监视图表DOM变化
296
+
130
297
  /**
131
- * echarts相关事件--
132
- * 鼠标移动激活数据时触发,动态更新Tips数据
133
- */
298
+ * @description: echarts数据高亮回调
299
+ * @param {any} data echarts数据
300
+ * @param {EChartsType} chart echarts实例
301
+ */
134
302
  const highlight = (data: any, chart: EChartsType) => {
135
303
  if (data) {
136
304
  // 图表内部移动
137
- activeIndex.value =
138
- typeof data?.batch[0]?.dataIndex === 'number' ? data?.batch[0]?.dataIndex : -1
305
+ activeIndex.value = typeof data?.batch[0]?.dataIndex === 'number' ? data?.batch[0]?.dataIndex : -1
139
306
  } else {
140
307
  // 移出图表
141
308
  const chartOptions: EChartsOption = chart.getOption() as EChartsOption
@@ -144,28 +311,181 @@
144
311
  }
145
312
  }
146
313
  }
314
+
315
+ /**
316
+ * @description: echarts数据缩放的相关参数
317
+ * @param {any} datazoomTimer 缩放回调函数的延时器
318
+ * @param {Number} datazoomTime 缩放回调函数的延时器的时间
319
+ * @param {Function} datazoom 数据缩放的回调函数
320
+ */
321
+ let datazoomTimer: any = null
322
+ const datazoomTime: number = 300
323
+ const datazoom = () => {
324
+ clearTimeout(datazoomTimer)
325
+ datazoomTimer = setTimeout(() => {
326
+ draw('line')
327
+ clearTimeout(datazoomTimer)
328
+ datazoomTimer = null
329
+ }, datazoomTime)
330
+ }
331
+
147
332
  /**
148
- * echarts相关事件--
149
- * 修改echarts图表配置渲染后,进行的回调
150
- */
151
- const afterInit = (chart: EChartsType) => {
333
+ * @description: echarts鼠标右击事件回调
334
+ */
335
+ let echartsContextMenuTimer: any = null
336
+ const echartsContextMenu = (params: ECElementEvent) => {
337
+ echartsContextMenuTimer = setTimeout(() => {
338
+ // 判定是否点击到的元素为额外画线
339
+ if (params.componentType === 'graphic') {
340
+ const { oncontextmenu } = params.info?.event ?? {}
341
+ oncontextmenu instanceof Function && oncontextmenu(params, params.info, menuData)
342
+ }
343
+ clearTimeout(echartsContextMenuTimer)
344
+ echartsContextMenuTimer = null
345
+ })
346
+ }
347
+
348
+ // 绑定事件
349
+ const addEventListener = () => {
350
+ echartsInstance.on('highlight', (data: any) => {
351
+ highlight(data, echartsInstance)
352
+ })
353
+ echartsInstance.on('globalout', () => {
354
+ highlight(null, echartsInstance)
355
+ })
356
+ echartsInstance.on('contextmenu', (params: ECElementEvent) => {
357
+ echartsContextMenu(params)
358
+ })
359
+ echartsInstance.on('datazoom', () => {
360
+ datazoom()
361
+ })
362
+ }
363
+
364
+ // 解绑事件
365
+ const removeEventListener = () => {
366
+ echartsInstance.off('highlight')
367
+ echartsInstance.off('globalout')
368
+ echartsInstance.off('contextmenu')
369
+ echartsInstance.off('datazoom')
370
+ }
152
371
 
372
+ /**
373
+ * @description: K线组件初始化
374
+ */
375
+ const init = async () => {
376
+ let Initializing: boolean | null = true // 正在初始化
377
+ // 1.初始化图表
378
+ echartsInstance = echarts.init(echartsRef.value) as EChartsType
379
+ // 2.绘制
380
+ await draw()
381
+ // 3.初始化右键菜单
382
+ menuData.value = props.defaultMenuData as MenuDataType
383
+ // 4.初始化数据激活索引
384
+ activeIndex.value =
385
+ ((echartsInstance.getOption().dataZoom as DataZoomComponentOption[])[0].endValue as number) ?? -1
386
+ // 5.进行图表事件绑定
387
+ addEventListener()
388
+ // 6.进行DOM监控,图表重加载
389
+ chartDomObserver = new ResizeObserver(() => {
390
+ if (Initializing) {
391
+ Initializing = null
392
+ return
393
+ }
394
+ echartsInstance.resize()
395
+ draw('line')
396
+ })
397
+ chartDomObserver.observe(echartsRef.value)
153
398
  }
154
399
 
155
400
  onMounted(() => {
156
401
  init()
157
402
  })
403
+
404
+ onUnmounted(() => {
405
+ // 1.解绑
406
+ removeEventListener()
407
+ // 2.销毁实例
408
+ echartsInstance.dispose()
409
+ // 3.取消监听图表DOM
410
+ chartDomObserver.disconnect()
411
+ chartDomObserver = null
412
+ })
158
413
  </script>
159
414
 
415
+ <template>
416
+ <!-- 神兔-K线组件 -->
417
+ <div class="st-kline">
418
+ <!-- Tips -->
419
+ <div class="st-kline-header">
420
+ <!-- 开, 高, 低, 收 -->
421
+ <Tips :data="kLineTips" />
422
+ <!-- 指标 -->
423
+ <Tips :data="indicatorTips" />
424
+ </div>
425
+ <!-- 图表 + 菜单 -->
426
+ <Contextmenu
427
+ class="st-kline-body"
428
+ @closeContextMenuCallBack="closeContextMenuCallBack"
429
+ >
430
+ <!-- 图表 -->
431
+ <div
432
+ ref="echartsRef"
433
+ :class="cursorPenVisible ? 'st-kline-chart cursorPen' : 'st-kline-chart'"
434
+ />
435
+ <!-- 菜单 -->
436
+ <template #popover>
437
+ <el-menu class="menu">
438
+ <el-menu-item
439
+ v-for="item in menuData"
440
+ :key="item.label"
441
+ :index="item.label"
442
+ class="menuItem"
443
+ @click="menuClick(item)"
444
+ >
445
+ {{ item.label }}
446
+ </el-menu-item>
447
+ </el-menu>
448
+ </template>
449
+ </Contextmenu>
450
+ </div>
451
+ </template>
452
+
453
+ <style lang="scss">
454
+ // 需要写在外面,不然会被Vue唯一标识符顶掉导致样式不生效
455
+ .cursorPen canvas {
456
+ cursor: url(./images/pen.png) -30 30, auto !important;
457
+ }
458
+ </style>
160
459
  <style lang="scss" scoped>
161
460
  .st-kline {
162
461
  width: 100%;
163
462
  height: 100%;
164
463
  background: #000;
165
- position: relative;
464
+ overflow-x: hidden;
465
+ position: relative; // 配合提供给菜单定位
466
+ // K线头部区域: Tips
166
467
  &-header {
468
+ height: 34px;
167
469
  position: absolute;
168
- padding: 2px 0 0 4px;
470
+ left: 66px;
471
+ }
472
+ &-body {
473
+ width: 100%;
474
+ height: 100%;
475
+ }
476
+ &-chart {
477
+ width: 100%;
478
+ height: 100%;
479
+ }
480
+ .menu {
481
+ border-radius: 4px;
482
+ overflow: hidden;
483
+ background-color: white;
484
+ border-right: 0;
485
+ .menuItem {
486
+ font-size: 16px;
487
+ height: 32px;
488
+ }
169
489
  }
170
490
  }
171
491
  </style>