st-comp 0.0.5 → 0.0.7

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.
@@ -1,8 +1,8 @@
1
- import { App } from "vue";
2
- import StKline from "./index.vue";
3
-
4
- export default {
5
- install(app: App) {
6
- app.component("st-kline", StKline);
7
- },
8
- };
1
+ import { App } from "vue";
2
+ import StKline from "./index.vue";
3
+
4
+ export default {
5
+ install(app: App) {
6
+ app.component("st-kline", StKline);
7
+ },
8
+ }
@@ -1,13 +1,15 @@
1
1
  <template>
2
2
  <div class="st-kline">
3
+ <!-- 顶部Tips -->
3
4
  <div class="st-kline-header">
4
5
  <Tips
5
6
  :activeIndex="activeIndex"
6
7
  :klineData="klineData"
7
8
  :indicatorData="indicatorData"
8
- :indicatorConfig="indicatorList[indicator]"
9
+ :indicatorConfig="indicatorConfigList[indicator]"
9
10
  />
10
11
  </div>
12
+ <!-- 图表 -->
11
13
  <st-echarts
12
14
  v-if="option"
13
15
  :option="option"
@@ -17,112 +19,197 @@
17
19
  dark: klineThemeDark,
18
20
  }"
19
21
  @highlight="highlight"
22
+ @afterInit="afterInit"
20
23
  />
21
24
  </div>
22
25
  </template>
23
26
 
24
27
  <script setup lang="ts">
25
- import { ref } from 'vue'
26
- import { getKline, getIndicator, getIndicatorList, getOption } from './utils'
27
28
  import dayjs from 'dayjs'
28
- import klineThemeLight from './kline_theme_light.json'
29
+ import { ref, onMounted } from 'vue'
30
+ import Tips from './components/Tips/index.vue'
29
31
  import klineThemeDark from './kline_theme_dark.json'
30
- import Tips from './components/TIps/index.vue'
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'
31
36
 
32
37
  const props = defineProps({
38
+ // 品种代码
33
39
  code: {
34
40
  type: String,
35
41
  required: true,
36
- }, // 股票代码
42
+ },
43
+ // 默认展示K线条数
37
44
  defaultCount: {
38
45
  type: Number,
39
- default: () => 500
40
- }, // 默认k线条数
41
- endTime: {
46
+ default: () => 500,
47
+ },
48
+ // 结束时间
49
+ tradeDate: {
42
50
  type: String,
43
51
  required: true,
44
- }, // 结束时间
52
+ },
53
+ // 周期名称
45
54
  frequency: {
46
55
  type: String,
47
56
  required: true,
48
- }, // 周期名称
57
+ },
58
+ // 指标线类型
49
59
  indicator: {
50
60
  type: String,
51
61
  required: true,
52
- }, // 指标线名称
62
+ },
53
63
  })
54
64
 
55
- const option = ref<any>(null)
56
- const klineData = ref([])
57
- const indicatorData = ref([])
58
- const indicatorList = ref({})
59
- const activeIndex = ref(0)
60
-
61
- // 图表高亮,用于记录当前高亮tooltip
62
- const highlight = (data, chart) => {
63
- if (data) {
64
- // 图表内部移动
65
- activeIndex.value = typeof data?.batch[0]?.dataIndex === 'number' ? data?.batch[0]?.dataIndex : -1
66
- } else {
67
- // 移出图表
68
- activeIndex.value = chart.getOption().dataZoom[0].endValue
69
- }
70
- }
65
+ const option = ref<any>(null) // 图表配置
66
+ const klineData = ref<KlineDataType>([]) // K线数据
67
+ const indicatorData = ref<IndicatorDataType>([]) // 指标线具体数据
68
+ const indicatorConfigList = ref<IndicatorConfigType>({}) // 指标线配置项
69
+ const activeIndex = ref(0) // 当前鼠标激活的数据索引
71
70
 
72
- const getKlineData = async() => {
73
- const { code, defaultCount, endTime, frequency } = props
74
- const res = await getKline(code, defaultCount, endTime, frequency)
71
+ // 获取K线数据
72
+ const getKlineData = async () => {
73
+ const { code, defaultCount, tradeDate, frequency } = props
74
+ const res = await getKline(code, defaultCount, tradeDate, frequency)
75
75
  return res.data
76
76
  }
77
77
 
78
- const getIndicatorData = async(klineData) => {
78
+ // 获取指标线具体数据
79
+ const getIndicatorData = async (klineData: KlineDataType) => {
79
80
  if (klineData.length === 0) return
80
81
  const { code, frequency, indicator } = props
81
82
  const start_date = dayjs(new Date(klineData[0][0] * 1000)).format('YYYY-MM-DD')
82
83
  const end_date = dayjs(new Date(klineData[klineData.length - 1][0] * 1000)).format('YYYY-MM-DD')
83
84
  const indicatorRes = await getIndicator(code, start_date, end_date, frequency, indicator)
84
- return indicatorRes.data.datetime.reduce((res, date, index) => {
85
- if (date >= klineData[0][0] && date <= klineData[klineData.length - 1][0]){
86
- return [...res, {
87
- ...Object.keys(indicatorRes.data).reduce((keyRes, key) => {
88
- return {
89
- ...keyRes,
90
- [key]: indicatorRes.data[key][index]
91
- }
92
- }, {})
93
- }]
94
- }
95
- return res
96
- }, [])
85
+ // 格式处理,只格式化获取处于K线之中的指标线数据
86
+ return indicatorRes.data.datetime
87
+ ? indicatorRes.data.datetime.reduce((res: IndicatorDataType, date: number, index: number) => {
88
+ if (date >= klineData[0][0] && date <= klineData[klineData.length - 1][0]) {
89
+ return [
90
+ ...res,
91
+ {
92
+ ...Object.keys(indicatorRes.data).reduce((keyRes, key) => {
93
+ return {
94
+ ...keyRes,
95
+ [key]: indicatorRes.data[key][index],
96
+ }
97
+ }, {}),
98
+ },
99
+ ]
100
+ }
101
+ return res
102
+ }, [])
103
+ : null
97
104
  }
98
105
 
99
- const getData = async() => {
106
+ // 初始化
107
+ const init = async () => {
108
+ // 1.获取K线数据
100
109
  klineData.value = await getKlineData()
110
+ // 2.默认Tips激活展示最后一条
101
111
  activeIndex.value = klineData.value.length - 1
112
+ // 3.获取指标线具体数据
102
113
  indicatorData.value = await getIndicatorData(klineData.value)
103
- const indicatorListRes = await getIndicatorList()
104
- indicatorList.value = indicatorListRes.data
105
- option.value = await getOption({
114
+ // 4.获取指标相关配置
115
+ indicatorConfigList.value = (await getIndicatorConfigList()).data
116
+ // 5.获取图表配置
117
+ option.value = await getOption({
106
118
  kLineData: klineData.value,
107
119
  indicatorData: indicatorData.value,
108
120
  indicator: props.indicator,
109
- indicatorList: indicatorList.value,
110
- defaultShowBarCount: 200
121
+ indicatorConfigList: indicatorConfigList.value,
122
+ defaultShowBarCount: 200,
111
123
  })
112
124
  }
113
125
 
114
- getData()
126
+ // 图表高亮,用于记录当前高亮tooltip
127
+ const highlight = (data: any, chart: EChartsType) => {
128
+ if (data) {
129
+ // 图表内部移动
130
+ activeIndex.value =
131
+ typeof data?.batch[0]?.dataIndex === 'number' ? data?.batch[0]?.dataIndex : -1
132
+ } else {
133
+ // 移出图表
134
+ const chartOptions: EChartsOption = chart.getOption() as EChartsOption
135
+ if (chartOptions.dataZoom instanceof Array) {
136
+ activeIndex.value = chartOptions.dataZoom[0].endValue as number
137
+ }
138
+ }
139
+ }
140
+ // 默认区域刷选
141
+ const afterInit = (chart: EChartsType) => {
142
+ /**
143
+ * 获取匹配用户选择的交易日期对应X轴的索引,用于刷选范围区域
144
+ * 匹配思路:
145
+ * 1.时间轴均转换为年月日字符串做对比,相等则算匹配成功
146
+ * 2.特殊周期K线需要特殊处理:
147
+ * [1w]:时间需要均转换成所处星期的星期五日期,判断它们星期五日期是否相等
148
+ * [1m,5m,15m,30m,60m]:包含前一交易日21点后的,排除掉交易日期21点后的
149
+ */
150
+ const { tradeDate, frequency } = props
151
+ const xAxisData = option.value.xAxis.data
152
+ let brushXIndexList: Array<number> = []
153
+ // 长周期判断
154
+ xAxisData.forEach((xItem: number, index: number) => {
155
+ // X轴年月日
156
+ const xItemDate = dayjs(xItem * 1000).format('YYYY-MM-DD')
157
+ xItemDate === tradeDate && brushXIndexList.push(index)
158
+ // 如果是周线,则需要判断两者周五日期是否相等
159
+ if (frequency === '1w') {
160
+ dayjs(xItemDate).day(5).format('YYYY-MM-DD') ===
161
+ dayjs(tradeDate).day(5).format('YYYY-MM-DD') && brushXIndexList.push(index)
162
+ }
163
+ })
164
+ const shortFreq = ['1m', '5m', '15m', '30m', '60m']
165
+ // 短周期判断
166
+ if (shortFreq.includes(frequency)) {
167
+ // 拿到上一个交易日
168
+ const lastTradeDateIndex = brushXIndexList[0] - 1
169
+ const lastTradeDate = dayjs(xAxisData[lastTradeDateIndex] * 1000).format('YYYY-MM-DD')
170
+ // 包含了上一个交易日21点之后的数据索引数组
171
+ const lastTrade21List: Array<number> = []
172
+ xAxisData.forEach((xItem: number, index: number) => {
173
+ if (
174
+ dayjs(xItem * 1000).format('YYYY-MM-DD') === lastTradeDate &&
175
+ dayjs(xItem * 1000).isSameOrAfter(`${lastTradeDate} 21:00:00`)
176
+ ) {
177
+ lastTrade21List.push(index)
178
+ }
179
+ })
180
+ // 排除匹配到交易日期21点之后的数据索引
181
+ const finallyDateIndex = brushXIndexList[brushXIndexList.length - 1]
182
+ const finallyDate = dayjs(xAxisData[finallyDateIndex] * 1000).format('YYYY-MM-DD 21:00:00')
183
+ brushXIndexList = [...lastTrade21List, ...brushXIndexList].filter(item => {
184
+ return dayjs(xAxisData[item] * 1000).isBefore(finallyDate)
185
+ })
186
+ }
187
+ chart.dispatchAction({
188
+ type: 'brush',
189
+ areas: [
190
+ {
191
+ brushType: 'lineX',
192
+ coordRange: [brushXIndexList[0], brushXIndexList.slice(-1)[0]],
193
+ xAxisIndex: 0,
194
+ },
195
+ ],
196
+ })
197
+ }
198
+
199
+ onMounted(() => {
200
+ init()
201
+ })
115
202
  </script>
116
203
 
117
204
  <style lang="scss" scoped>
118
- .st-kline {
119
- width: 100%;
120
- height: 100%;
121
- background: #000;
122
- position: relative;
123
- &-header {
124
- position: absolute;
125
- padding: 2px 0 0 4px;
205
+ .st-kline {
206
+ width: 100%;
207
+ height: 100%;
208
+ background: #000;
209
+ position: relative;
210
+ &-header {
211
+ position: absolute;
212
+ padding: 2px 0 0 4px;
213
+ }
126
214
  }
127
- }
128
215
  </style>
@@ -1,9 +1,13 @@
1
+ // K线数据类型
1
2
  export type KlineDataItem = [number, number, number, number, number, number, number, number, number]
3
+ export type KlineDataType = Array<KlineDataItem>
2
4
 
5
+ // 指标线数据类型
3
6
  export interface IndicatorDataItem {
4
7
  datetime: number;
5
8
  [key: string]: number;
6
9
  }
10
+ export type IndicatorDataType = Array<IndicatorDataItem>
7
11
 
8
12
  export interface InOption {
9
13
  kLineData: KlineDataItem[];
@@ -12,3 +16,19 @@ export interface InOption {
12
16
  indicatorList: any;
13
17
  defaultShowBarCount: number;
14
18
  }
19
+
20
+ // 指标线配置项数据类型
21
+ export interface IndicatorConfigType {
22
+ [key: string]: {
23
+ [key: string]: string
24
+ };
25
+ }
26
+
27
+ // 获取图表配置项入参类型
28
+ export interface InOption {
29
+ kLineData: KlineDataType;
30
+ indicatorData: IndicatorDataType;
31
+ indicator: string;
32
+ indicatorConfigList: any;
33
+ defaultShowBarCount: number;
34
+ }
@@ -1,26 +1,113 @@
1
1
  import axios from 'axios'
2
+ import dayjs from 'dayjs'
3
+ import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
2
4
  import type { KlineDataItem, IndicatorDataItem, InOption } from './type.d.ts'
5
+ dayjs.extend(isSameOrAfter)
3
6
 
4
- // 获取k线数据
5
- export const getKline = (code: string, bar_count: number, dt: string, frequency: string) => {
6
- return axios(`http://116.62.161.92:8005/history_kline?code=${code}&bar_count=${bar_count}&dt=${dt}&frequency=${frequency}`)
7
+ /**
8
+ * @description: 获取固定间隔后的交易日
9
+ * @param {string} currentDate 初始交易日期
10
+ * @param {number | undefined} days 间隔天数
11
+ * @return {*}
12
+ */
13
+ const getTradeByAddDays = (currentDate: string, days: number | undefined) => {
14
+ return axios({
15
+ headers: {
16
+ Token: '8c9e48f3bfff810c3403829233f76515',
17
+ },
18
+ method: 'post',
19
+ url: '/proxy/review/review/getTradeByAddDays',
20
+ data: {
21
+ currentDate,
22
+ days,
23
+ },
24
+ })
7
25
  }
8
26
 
9
- // 获取指标线列表
10
- export const getIndicatorList = () => {
11
- return axios('http://116.62.161.92:8005/get_indicator')
27
+ /**
28
+ * @description: 获取K线数据
29
+ * @param {string} code 品种代码
30
+ * @param {number} bar_count 请求数据条数
31
+ * @param {string} tradeDate 交易日期
32
+ * @param {string} frequency 周期
33
+ * @return {*}
34
+ */
35
+ export const getKline = async (
36
+ code: string,
37
+ bar_count: number,
38
+ tradeDate: string,
39
+ frequency: string
40
+ ) => {
41
+ // 周期对应的间隔交易日
42
+ const timeIntervalMap = new Map([
43
+ ['1m', 1],
44
+ ['5m', 1],
45
+ ['15m', 4],
46
+ ['30m', 8],
47
+ ['60m', 16],
48
+ ['1d', 80],
49
+ ['1w', 80],
50
+ ])
51
+ // 根据周期判断逻辑请求到对应的间隔末尾交易日
52
+ const interval = timeIntervalMap.get(frequency)
53
+ const res = await getTradeByAddDays(tradeDate, interval)
54
+ const nowDate = dayjs().format('YYYY-MM-DD')
55
+ const resDate = res.data.body.tradeDate.split(' ')[0]
56
+ let dt: null | string = null
57
+ // 判断接口获取到的交易日是否大于等于今日,如果为真,则以今日日期为末尾
58
+ if (dayjs(resDate).isSameOrAfter(nowDate)) {
59
+ dt = dayjs().format('YYYY-MM-DD HH:mm:ss')
60
+ } else {
61
+ dt = `${resDate} 23:59:59`
62
+ }
63
+ return axios(
64
+ `http://116.62.161.92:8005/history_kline?code=${code}&bar_count=${bar_count}&dt=${dt}&frequency=${frequency}`
65
+ )
12
66
  }
13
67
 
14
- // 获取指标线数据
15
- export const getIndicator = (code: string, start_date: string, end_date: string, frequency: string, indicator: string) => {
16
- return axios(`http://116.62.161.92:8005/realtime_indicator?code=${code}&start_date=${start_date}&end_date=${end_date}&indicator=${indicator}&frequency=${frequency}`)
68
+ /**
69
+ * @description: 获取指标线下拉列表类型数据以及相关样式配置
70
+ * @return {*}
71
+ */
72
+ export const getIndicatorConfigList = () => {
73
+ return axios('http://116.62.161.92:8005/get_indicator')
17
74
  }
18
75
 
76
+ /**
77
+ * @description: 获取指标线具体数据
78
+ * @param {string} code 品种代码
79
+ * @param {string} start_date 开始日期
80
+ * @param {string} end_date 结束日期
81
+ * @param {string} frequency 周期
82
+ * @param {string} indicator 指标类型
83
+ * @return {*}
84
+ */
85
+ export const getIndicator = (
86
+ code: string,
87
+ start_date: string,
88
+ end_date: string,
89
+ frequency: string,
90
+ indicator: string
91
+ ) => {
92
+ return axios(
93
+ `http://116.62.161.92:8005/realtime_indicator?code=${code}&start_date=${start_date}&end_date=${end_date}&indicator=${indicator}&frequency=${frequency}`
94
+ )
95
+ }
19
96
 
97
+ /**
98
+ * @description: 格式化数字:保留后三位小数
99
+ * @param {number} value
100
+ * @return {*}
101
+ */
20
102
  export const formatValue = (value: number) => {
21
103
  return value || value === 0 ? Math.round(value * 1000) / 1000 : null
22
104
  }
23
105
 
106
+ /**
107
+ * @description: 格式化价格:万 || 亿 || 万亿
108
+ * @param {number} value
109
+ * @return {*}
110
+ */
24
111
  export const formatPrice = (value: number) => {
25
112
  if (value >= 1000000000000) {
26
113
  return `${(value / 1000000000000).toFixed(2)}万亿`
@@ -33,14 +120,13 @@ export const formatPrice = (value: number) => {
33
120
  }
34
121
  }
35
122
 
36
- export const getOption = async(data: InOption) => {
37
- const {
38
- kLineData,
39
- indicatorData,
40
- indicator,
41
- indicatorList,
42
- defaultShowBarCount
43
- } = data
123
+ /**
124
+ * @description: 格式化K线图配置项
125
+ * @param {InOption} data
126
+ * @return {*}
127
+ */
128
+ export const getOption = async (data: InOption) => {
129
+ const { kLineData, indicatorData, indicator, indicatorConfigList, defaultShowBarCount } = data
44
130
  // 处理k线数据,x轴数据
45
131
  const candlestickData: any[] = [] // k线数据
46
132
  const xAxisData: number[] = [] // x轴数据
@@ -57,25 +143,29 @@ export const getOption = async(data: InOption) => {
57
143
  })
58
144
 
59
145
  // 处理指标线数据
60
- const indicatorInfo = indicatorList[indicator]
61
- const lineSeries = Object.keys(indicatorData[0]).filter(i => i !== 'datetime').map((key: string) => {
62
- let color = 'rgba(238, 238, 238, 0.5)'
63
- if (indicatorInfo && indicatorInfo[key]) {
64
- color = `#${indicatorInfo[key].split('#')[1]}`
65
- }
66
- return {
67
- name: key,
68
- type: 'line',
69
- symbol: 'none',
70
- data: indicatorData.map(i => i[key]),
71
- lineStyle: {
72
- width: 1,
73
- },
74
- itemStyle: {
75
- color,
76
- },
77
- }
78
- })
146
+ const indicatorInfo = indicatorConfigList[indicator]
147
+ const lineSeries = indicatorData
148
+ ? Object.keys(indicatorData[0])
149
+ .filter(i => i !== 'datetime')
150
+ .map((key: string) => {
151
+ let color = 'rgba(238, 238, 238, 0.5)'
152
+ if (indicatorInfo && indicatorInfo[key]) {
153
+ color = `#${indicatorInfo[key].split('#')[1]}`
154
+ }
155
+ return {
156
+ name: key,
157
+ type: 'line',
158
+ symbol: 'none',
159
+ data: indicatorData.map(i => i[key]),
160
+ lineStyle: {
161
+ width: 1,
162
+ },
163
+ itemStyle: {
164
+ color,
165
+ },
166
+ }
167
+ })
168
+ : []
79
169
 
80
170
  return {
81
171
  dataset: {
@@ -85,9 +175,9 @@ export const getOption = async(data: InOption) => {
85
175
  },
86
176
  },
87
177
  grid: {
88
- left: '60px',
89
- top: '40px',
90
- right: '60px',
178
+ left: '50px',
179
+ top: '50px',
180
+ right: '30px',
91
181
  bottom: '30px',
92
182
  },
93
183
  tooltip: {
@@ -98,18 +188,23 @@ export const getOption = async(data: InOption) => {
98
188
  label: {
99
189
  formatter: (data: any) => {
100
190
  const { axisDimension, value } = data
101
- if(axisDimension === 'x') {
102
- return value
191
+ if (axisDimension === 'x') {
192
+ return dayjs(new Date(value * 1000)).format('YYYY-MM-DD HH:mm')
103
193
  } else {
104
194
  return String(formatValue(value))
105
195
  }
106
- }
196
+ },
107
197
  },
108
198
  },
109
199
  },
110
200
  xAxis: {
111
201
  type: 'category',
112
202
  data: xAxisData,
203
+ axisLine: {
204
+ lineStyle: {
205
+ color: 'red',
206
+ },
207
+ },
113
208
  splitLine: {
114
209
  show: true,
115
210
  lineStyle: {
@@ -118,6 +213,7 @@ export const getOption = async(data: InOption) => {
118
213
  },
119
214
  axisLabel: {
120
215
  show: true,
216
+ formatter: data => dayjs(new Date(data * 1000)).format('YYYY-MM-DD HH:mm'),
121
217
  },
122
218
  },
123
219
  yAxis: [
@@ -126,6 +222,9 @@ export const getOption = async(data: InOption) => {
126
222
  min: 'dataMin',
127
223
  axisLine: {
128
224
  show: true,
225
+ lineStyle: {
226
+ color: 'red',
227
+ },
129
228
  },
130
229
  splitLine: {
131
230
  show: true,
@@ -134,7 +233,7 @@ export const getOption = async(data: InOption) => {
134
233
  },
135
234
  },
136
235
  axisLabel: {
137
- formatter: (value: number) => formatValue(value)
236
+ formatter: (value: number) => formatValue(value),
138
237
  },
139
238
  },
140
239
  ],
@@ -154,5 +253,20 @@ export const getOption = async(data: InOption) => {
154
253
  },
155
254
  ...lineSeries,
156
255
  ],
256
+ toolbox: {
257
+ show: false,
258
+ },
259
+ brush: {
260
+ xAxisIndex: 'all',
261
+ brushLink: 'all',
262
+ transformable: false,
263
+ outOfBrush: {
264
+ colorAlpha: 1,
265
+ },
266
+ brushStyle: {
267
+ color: 'rgba(120,140,180,0)',
268
+ borderColor: 'rgba(255,255,255,0.4)',
269
+ },
270
+ },
157
271
  }
158
- }
272
+ }
@@ -1,8 +1,8 @@
1
- import { App } from "vue";
2
- import StList from "./index.vue";
3
-
4
- export default {
5
- install(app: App) {
6
- app.component("StList", StList);
7
- },
8
- };
1
+ import { App } from "vue";
2
+ import StList from "./index.vue";
3
+
4
+ export default {
5
+ install(app: App) {
6
+ app.component("st-list", StList);
7
+ },
8
+ }
@@ -1,8 +1,8 @@
1
- import { App } from "vue";
2
- import StTable from "./index.vue";
3
-
4
- export default {
5
- install(app: App) {
6
- app.component("st-table", StTable);
7
- },
8
- };
1
+ import { App } from "vue";
2
+ import StTable from "./index.vue";
3
+
4
+ export default {
5
+ install(app: App) {
6
+ app.component("st-table", StTable);
7
+ },
8
+ }