|
|
@@ -1,133 +0,0 @@
|
|
|
-<template>
|
|
|
- <div class="kline-wrapper" :style="{ height: height }">
|
|
|
- <div ref="chartContainer" class="kline-chart"></div>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script setup>
|
|
|
-import { onMounted, onBeforeUnmount, ref, watch, nextTick, toRaw } from 'vue'
|
|
|
-import * as klinecharts from 'klinecharts'
|
|
|
-
|
|
|
-const props = defineProps({
|
|
|
- data: { type: Array, default: () => [] },
|
|
|
- height: { type: String, default: '100%' },
|
|
|
- precision: { type: Object, default: () => ({ price: 2, volume: 2 }) },
|
|
|
- colors: {
|
|
|
- type: Object,
|
|
|
- default: () => ({
|
|
|
- up: '#2EBD85',
|
|
|
- down: '#F6465D',
|
|
|
- grid: '#F2F4F6',
|
|
|
- text: '#929AA5',
|
|
|
- targetLine: '#4A6EF5'
|
|
|
- })
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-const chartContainer = ref(null)
|
|
|
-let chartInstance = null
|
|
|
-
|
|
|
-const initChart = () => {
|
|
|
- if (!chartContainer.value) return
|
|
|
- if (chartInstance) klinecharts.dispose(chartContainer.value)
|
|
|
-
|
|
|
- chartInstance = klinecharts.init(chartContainer.value)
|
|
|
-
|
|
|
- // 设置精度
|
|
|
- const { price, volume } = props.precision
|
|
|
- if (chartInstance.setPriceVolumePrecision) {
|
|
|
- chartInstance.setPriceVolumePrecision(price, volume)
|
|
|
- } else if (chartInstance.setPrecision) {
|
|
|
- chartInstance.setPrecision(price, volume)
|
|
|
- }
|
|
|
-
|
|
|
- // 样式配置
|
|
|
- const { up, down, grid, text, targetLine } = props.colors
|
|
|
- chartInstance.setStyleOptions({
|
|
|
- grid: { show: true, horizontal: { show: true, size: 1, color: grid, style: 'dash', dashValue: [5, 5] }, vertical: { show: false } },
|
|
|
- candle: {
|
|
|
- type: 'candle_solid',
|
|
|
- bar: { upColor: up, downColor: down, noChangeColor: up },
|
|
|
- priceMark: {
|
|
|
- show: true,
|
|
|
- last: { show: true, upColor: up, downColor: down, line: { show: true, style: 'dash' }, text: { show: true, color: '#FFF', paddingLeft: 4, paddingRight: 4, borderRadius: 2 } }
|
|
|
- },
|
|
|
- tooltip: { showRule: 'follow_cross', showType: 'rect', dataSource: 'none' }
|
|
|
- },
|
|
|
- xAxis: { axisLine: { show: false }, tickLine: { show: false }, tickText: { color: text, size: 10, paddingTop: 8 } },
|
|
|
- yAxis: { inside: true, axisLine: { show: false }, tickLine: { show: false }, tickText: { color: text, size: 10, paddingLeft: 8 } },
|
|
|
- })
|
|
|
-
|
|
|
- chartInstance.createTechnicalIndicator('VOL', false, { id: 'pane_1', heightRatio: 0.2 })
|
|
|
-
|
|
|
- // 初始加载
|
|
|
- if (props.data && props.data.length > 0) {
|
|
|
- chartInstance.applyNewData(toRaw(props.data))
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// --- 🔥 核心修复:智能判断是“更新”还是“重置” ---
|
|
|
-watch(() => props.data, (newData) => {
|
|
|
- if (!chartInstance) return
|
|
|
-
|
|
|
- const rawData = toRaw(newData)
|
|
|
- const currentList = chartInstance.getDataList()
|
|
|
-
|
|
|
- // 1. 如果新数据为空,清空图表
|
|
|
- if (rawData.length === 0) {
|
|
|
- chartInstance.clearData()
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- // 2. 如果当前图表为空,直接加载
|
|
|
- if (currentList.length === 0) {
|
|
|
- chartInstance.applyNewData(rawData)
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- // 3. 🔥 关键判断:
|
|
|
- // 如果第一根 K 线的时间戳变了,说明切换了周期或币种 -> 全量重置
|
|
|
- const firstOld = currentList[0]
|
|
|
- const firstNew = rawData[0]
|
|
|
- if (firstOld.timestamp !== firstNew.timestamp) {
|
|
|
- chartInstance.applyNewData(rawData)
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- // 4. 如果第一根时间没变,说明是实时跳动或追加 -> 增量更新
|
|
|
- if (rawData.length > 0) {
|
|
|
- const lastData = rawData[rawData.length - 1]
|
|
|
- chartInstance.updateData(lastData)
|
|
|
- }
|
|
|
-}, { deep: true })
|
|
|
-
|
|
|
-// 监听精度
|
|
|
-watch(() => props.precision, (val) => {
|
|
|
- if (chartInstance) {
|
|
|
- if (chartInstance.setPriceVolumePrecision) chartInstance.setPriceVolumePrecision(val.price, val.volume)
|
|
|
- else if (chartInstance.setPrecision) chartInstance.setPrecision(val.price, val.volume)
|
|
|
- }
|
|
|
-}, { deep: true })
|
|
|
-
|
|
|
-onMounted(() => {
|
|
|
- nextTick(() => initChart())
|
|
|
- window.addEventListener('resize', handleResize)
|
|
|
-})
|
|
|
-
|
|
|
-onBeforeUnmount(() => {
|
|
|
- window.removeEventListener('resize', handleResize)
|
|
|
- if (chartInstance) {
|
|
|
- klinecharts.dispose(chartContainer.value)
|
|
|
- chartInstance = null
|
|
|
- }
|
|
|
-})
|
|
|
-
|
|
|
-const handleResize = () => {
|
|
|
- if (chartInstance) chartInstance.resize()
|
|
|
-}
|
|
|
-</script>
|
|
|
-
|
|
|
-<style scoped>
|
|
|
-.kline-wrapper { width: 100%; position: relative; }
|
|
|
-.kline-chart { width: 100%; height: 100%; }
|
|
|
-</style>
|