@363045841yyt/klinechart 0.8.3 → 0.8.5
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/dist/components/BatchStockDialog.vue.d.ts +13 -0
- package/dist/components/BatchStockDialog.vue.d.ts.map +1 -0
- package/dist/components/CompareSymbolSelector.vue.d.ts.map +1 -1
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/ExportProgressDialog.vue.d.ts +15 -0
- package/dist/components/ExportProgressDialog.vue.d.ts.map +1 -0
- package/dist/components/IndicatorSelector.vue.d.ts.map +1 -1
- package/dist/components/KLineChart.vue.d.ts +5 -9
- package/dist/components/KLineChart.vue.d.ts.map +1 -1
- package/dist/components/LeftToolbar.vue.d.ts.map +1 -1
- package/dist/components/SymbolSelector.vue.d.ts.map +1 -1
- package/dist/components/TopToolbar.vue.d.ts.map +1 -1
- package/dist/composables/chart/useChartTheme.d.ts +329 -0
- package/dist/composables/chart/useChartTheme.d.ts.map +1 -0
- package/dist/composables/chart/useDrawingManager.d.ts +86 -0
- package/dist/composables/chart/useDrawingManager.d.ts.map +1 -0
- package/dist/composables/chart/useIndicatorManager.d.ts +38 -0
- package/dist/composables/chart/useIndicatorManager.d.ts.map +1 -0
- package/dist/composables/chart/useRangeSelection.d.ts +65 -0
- package/dist/composables/chart/useRangeSelection.d.ts.map +1 -0
- package/dist/composables/useTeleportedPopup.d.ts +8 -0
- package/dist/composables/useTeleportedPopup.d.ts.map +1 -0
- package/dist/index.cjs +9 -2
- package/dist/index.css +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1769 -1090
- package/dist/tools/calcRangeOverlayPixel.d.ts +15 -0
- package/dist/tools/calcRangeOverlayPixel.d.ts.map +1 -0
- package/dist/tools/getKLineIndexByTimestamp.d.ts +4 -0
- package/dist/tools/getKLineIndexByTimestamp.d.ts.map +1 -0
- package/dist/web-component.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/BatchStockDialog.vue +293 -0
- package/src/components/CompareSymbolSelector.vue +35 -8
- package/src/components/Dropdown.vue +42 -19
- package/src/components/ExportProgressDialog.vue +226 -0
- package/src/components/IndicatorSelector.vue +13 -5
- package/src/components/KLineChart.vue +329 -399
- package/src/components/LeftToolbar.vue +2 -1
- package/src/components/SymbolSelector.vue +35 -8
- package/src/components/TopToolbar.vue +55 -2
- package/src/composables/chart/useChartTheme.ts +86 -0
- package/src/composables/chart/useDrawingManager.ts +67 -0
- package/src/composables/chart/useIndicatorManager.ts +307 -0
- package/src/composables/chart/useRangeSelection.ts +417 -0
- package/src/composables/useTeleportedPopup.ts +33 -0
- package/src/index.ts +41 -14
- package/src/tools/calcRangeOverlayPixel.ts +28 -0
- package/src/tools/getKLineIndexByTimestamp.ts +40 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Teleport :to="teleportTarget">
|
|
3
|
+
<Transition name="overlay">
|
|
4
|
+
<div v-if="progress" class="export-overlay">
|
|
5
|
+
<Transition name="modal">
|
|
6
|
+
<div class="export-modal" @click.stop>
|
|
7
|
+
<div class="export-header">
|
|
8
|
+
<span class="export-title">导出数据</span>
|
|
9
|
+
<button class="export-close-btn" @click="emit('close')">
|
|
10
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
11
|
+
<path d="M18 6L6 18M6 6l12 12" />
|
|
12
|
+
</svg>
|
|
13
|
+
</button>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="export-body">
|
|
16
|
+
<div class="export-label">{{ progress.label }}</div>
|
|
17
|
+
<div class="export-bar-track">
|
|
18
|
+
<div
|
|
19
|
+
class="export-bar-fill"
|
|
20
|
+
:style="{ width: pct + '%' }"
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="export-counter">{{ progress.current }} / {{ progress.total }}</div>
|
|
24
|
+
<button
|
|
25
|
+
v-if="progress.current === progress.total"
|
|
26
|
+
class="export-done-btn"
|
|
27
|
+
@click="emit('close')"
|
|
28
|
+
>完成</button>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</Transition>
|
|
32
|
+
</div>
|
|
33
|
+
</Transition>
|
|
34
|
+
</Teleport>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import { computed } from 'vue'
|
|
39
|
+
import { useFullscreenTeleportTarget } from '../composables/useFullscreenTeleportTarget'
|
|
40
|
+
|
|
41
|
+
const props = defineProps<{
|
|
42
|
+
progress: { current: number; total: number; label: string } | null
|
|
43
|
+
}>()
|
|
44
|
+
|
|
45
|
+
const emit = defineEmits<{
|
|
46
|
+
close: []
|
|
47
|
+
}>()
|
|
48
|
+
|
|
49
|
+
const teleportTarget = useFullscreenTeleportTarget()
|
|
50
|
+
|
|
51
|
+
const pct = computed(() => {
|
|
52
|
+
if (!props.progress || props.progress.total <= 0) return 0
|
|
53
|
+
return Math.min(100, Math.round((props.progress.current / props.progress.total) * 100))
|
|
54
|
+
})
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<style scoped>
|
|
58
|
+
.export-overlay {
|
|
59
|
+
position: fixed;
|
|
60
|
+
inset: 0;
|
|
61
|
+
background: rgba(0, 0, 0, 0.3);
|
|
62
|
+
backdrop-filter: blur(4px);
|
|
63
|
+
padding: 24px;
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
justify-content: center;
|
|
67
|
+
z-index: 1100;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.export-modal {
|
|
71
|
+
background: var(--klc-color-tag-bg-white);
|
|
72
|
+
border: 1px solid var(--klc-color-border-button);
|
|
73
|
+
border-radius: 10px;
|
|
74
|
+
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.15);
|
|
75
|
+
min-width: 320px;
|
|
76
|
+
max-width: 380px;
|
|
77
|
+
width: min(88vw, 380px);
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.export-header {
|
|
84
|
+
display: flex;
|
|
85
|
+
justify-content: space-between;
|
|
86
|
+
align-items: center;
|
|
87
|
+
padding: 14px 18px 0 20px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.export-title {
|
|
91
|
+
font-size: 15px;
|
|
92
|
+
font-weight: 600;
|
|
93
|
+
color: var(--klc-color-foreground);
|
|
94
|
+
line-height: 1.35;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.export-close-btn {
|
|
98
|
+
background: var(--klc-color-tag-bg-white);
|
|
99
|
+
border: 1px solid var(--klc-color-border-button);
|
|
100
|
+
border-radius: 7px;
|
|
101
|
+
width: 30px;
|
|
102
|
+
height: 30px;
|
|
103
|
+
display: flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
justify-content: center;
|
|
106
|
+
cursor: pointer;
|
|
107
|
+
color: var(--klc-color-axis-text);
|
|
108
|
+
transition:
|
|
109
|
+
background 0.15s,
|
|
110
|
+
color 0.15s,
|
|
111
|
+
border-color 0.15s;
|
|
112
|
+
padding: 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.export-close-btn:hover {
|
|
116
|
+
background: var(--klc-color-tag-bg-hover);
|
|
117
|
+
color: var(--klc-color-foreground);
|
|
118
|
+
border-color: var(--klc-color-axis-line);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.export-close-btn svg {
|
|
122
|
+
width: 14px;
|
|
123
|
+
height: 14px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.export-body {
|
|
127
|
+
padding: 16px 20px 24px;
|
|
128
|
+
display: flex;
|
|
129
|
+
flex-direction: column;
|
|
130
|
+
gap: 10px;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.export-label {
|
|
134
|
+
font-size: 13px;
|
|
135
|
+
color: var(--klc-color-axis-text);
|
|
136
|
+
line-height: 1.4;
|
|
137
|
+
white-space: nowrap;
|
|
138
|
+
overflow: hidden;
|
|
139
|
+
text-overflow: ellipsis;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.export-bar-track {
|
|
143
|
+
width: 100%;
|
|
144
|
+
height: 6px;
|
|
145
|
+
background: var(--klc-color-grid-major);
|
|
146
|
+
border-radius: 999px;
|
|
147
|
+
overflow: hidden;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.export-bar-fill {
|
|
151
|
+
height: 100%;
|
|
152
|
+
background: var(--klc-color-foreground);
|
|
153
|
+
border-radius: 999px;
|
|
154
|
+
transition: width 0.25s ease;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.export-counter {
|
|
158
|
+
font-size: 12px;
|
|
159
|
+
color: var(--klc-color-axis-text);
|
|
160
|
+
text-align: right;
|
|
161
|
+
font-variant-numeric: tabular-nums;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.export-done-btn {
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
justify-content: center;
|
|
168
|
+
height: 32px;
|
|
169
|
+
padding: 0 20px;
|
|
170
|
+
border-radius: 7px;
|
|
171
|
+
font-size: 13px;
|
|
172
|
+
font-weight: 500;
|
|
173
|
+
cursor: pointer;
|
|
174
|
+
border: 1px solid transparent;
|
|
175
|
+
background: var(--klc-color-foreground);
|
|
176
|
+
border-color: var(--klc-color-foreground);
|
|
177
|
+
color: var(--klc-color-background);
|
|
178
|
+
align-self: center;
|
|
179
|
+
transition:
|
|
180
|
+
background 0.15s,
|
|
181
|
+
box-shadow 0.15s,
|
|
182
|
+
transform 0.15s;
|
|
183
|
+
line-height: 1;
|
|
184
|
+
white-space: nowrap;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.export-done-btn:hover {
|
|
188
|
+
background: var(--klc-color-foreground);
|
|
189
|
+
border-color: var(--klc-color-foreground);
|
|
190
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
|
|
191
|
+
transform: translateY(-1px);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.export-done-btn:active {
|
|
195
|
+
transform: translateY(0);
|
|
196
|
+
box-shadow: none;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.overlay-enter-active,
|
|
200
|
+
.overlay-leave-active {
|
|
201
|
+
transition: opacity 0.2s ease;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.overlay-enter-from,
|
|
205
|
+
.overlay-leave-to {
|
|
206
|
+
opacity: 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.modal-enter-active {
|
|
210
|
+
transition: all 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.modal-leave-active {
|
|
214
|
+
transition: all 0.16s ease-in;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.modal-enter-from {
|
|
218
|
+
opacity: 0;
|
|
219
|
+
transform: scale(0.96) translateY(-10px);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.modal-leave-to {
|
|
223
|
+
opacity: 0;
|
|
224
|
+
transform: scale(0.98) translateY(8px);
|
|
225
|
+
}
|
|
226
|
+
</style>
|
|
@@ -219,6 +219,8 @@ import {
|
|
|
219
219
|
allIndicators,
|
|
220
220
|
findIndicator,
|
|
221
221
|
type Indicator,
|
|
222
|
+
loadBuiltinIndicators,
|
|
223
|
+
isBuiltinIndicatorsLoaded,
|
|
222
224
|
} from '@363045841yyt/klinechart-core/controllers'
|
|
223
225
|
|
|
224
226
|
const props = defineProps<{
|
|
@@ -233,7 +235,7 @@ const emit = defineEmits<{
|
|
|
233
235
|
}>()
|
|
234
236
|
|
|
235
237
|
// ── 将 Indicator[] 转换为 IndicatorDefinition[] ──
|
|
236
|
-
function toIndicatorDefinitions(source:
|
|
238
|
+
function toIndicatorDefinitions(source: Indicator[]): IndicatorDefinition[] {
|
|
237
239
|
return source.map((i) => ({
|
|
238
240
|
id: i.id,
|
|
239
241
|
label: i.label,
|
|
@@ -253,9 +255,7 @@ function toIndicatorDefinitions(source: typeof allIndicators): IndicatorDefiniti
|
|
|
253
255
|
}
|
|
254
256
|
|
|
255
257
|
// ── Controller ──
|
|
256
|
-
const controller = createIndicatorSelectorController(
|
|
257
|
-
catalog: toIndicatorDefinitions(allIndicators),
|
|
258
|
-
})
|
|
258
|
+
const controller = createIndicatorSelectorController()
|
|
259
259
|
|
|
260
260
|
// ── 从 Controller Signal 桥接的 Vue 响应式状态 ──
|
|
261
261
|
const menuOpen = coreSignalToVueRef(controller.menuOpen)
|
|
@@ -267,7 +267,15 @@ const hasSearchResults = computed(
|
|
|
267
267
|
() => filteredMain.value.length > 0 || filteredSub.value.length > 0,
|
|
268
268
|
)
|
|
269
269
|
|
|
270
|
-
const
|
|
270
|
+
const catalog = coreSignalToVueRef(controller.catalog)
|
|
271
|
+
const catalogLen = computed(() => catalog.value.length)
|
|
272
|
+
|
|
273
|
+
onMounted(async () => {
|
|
274
|
+
if (!isBuiltinIndicatorsLoaded()) {
|
|
275
|
+
await loadBuiltinIndicators()
|
|
276
|
+
}
|
|
277
|
+
controller.catalog.set(toIndicatorDefinitions(allIndicators()))
|
|
278
|
+
})
|
|
271
279
|
|
|
272
280
|
// ── 本地 UI 状态(非 Controller 管理的纯 UI 状态) ──
|
|
273
281
|
const paramsVisible = ref(false)
|