Files
art-design/src/components/core/charts/art-k-line-chart/index.vue
T
2026-01-10 10:10:57 +08:00

159 lines
3.6 KiB
Vue

<!-- k线图表 -->
<template>
<div
ref="chartRef"
class="relative w-full"
:style="{ height: props.height }"
v-loading="props.loading"
></div>
</template>
<script setup lang="ts">
import type { EChartsOption } from '@/plugins/echarts'
import { useChartOps, useChartComponent } from '@/hooks/core/useChart'
import type { KLineChartProps } from '@/types/component/chart'
defineOptions({ name: 'ArtKLineChart' })
const props = withDefaults(defineProps<KLineChartProps>(), {
// 基础配置
height: useChartOps().chartHeight,
loading: false,
isEmpty: false,
colors: () => useChartOps().colors,
// 数据配置
data: () => [],
showDataZoom: false,
dataZoomStart: 0,
dataZoomEnd: 100
})
// 获取实际使用的颜色
const getActualColors = () => {
const defaultUpColor = '#4C87F3'
const defaultDownColor = '#8BD8FC'
return {
upColor: props.colors?.[0] || defaultUpColor,
downColor: props.colors?.[1] || defaultDownColor
}
}
// 使用新的图表组件抽象
const {
chartRef,
getAxisLineStyle,
getAxisLabelStyle,
getAxisTickStyle,
getSplitLineStyle,
getAnimationConfig,
getTooltipStyle
} = useChartComponent({
props,
checkEmpty: () => {
return (
!props.data?.length ||
props.data.every(
(item) =>
item.open === 0 && item.close === 0 && item.high === 0 && item.low === 0
)
)
},
watchSources: [
() => props.data,
() => props.colors,
() => props.showDataZoom,
() => props.dataZoomStart,
() => props.dataZoomEnd
],
generateOptions: (): EChartsOption => {
const { upColor, downColor } = getActualColors()
return {
grid: {
top: 20,
right: 20,
bottom: props.showDataZoom ? 80 : 20,
left: 20,
containLabel: true
},
tooltip: getTooltipStyle('axis', {
axisPointer: {
type: 'cross'
},
formatter: (params: Array<{ name: string; data: number[] }>) => {
const param = params[0]
const data = param.data
return `
<div style="padding: 5px;">
<div><strong>时间:</strong>${param.name}</div>
<div><strong>开盘:</strong>${data[0]}</div>
<div><strong>收盘:</strong>${data[1]}</div>
<div><strong>最低:</strong>${data[2]}</div>
<div><strong>最高:</strong>${data[3]}</div>
</div>
`
}
}),
xAxis: {
type: 'category',
data: props.data.map((item) => item.time),
axisTick: getAxisTickStyle(),
axisLine: getAxisLineStyle(true),
axisLabel: getAxisLabelStyle(true)
},
yAxis: {
type: 'value',
scale: true,
axisLabel: getAxisLabelStyle(true),
axisLine: getAxisLineStyle(true),
splitLine: getSplitLineStyle(true)
},
series: [
{
type: 'candlestick',
data: props.data.map((item) => [
item.open,
item.close,
item.low,
item.high
]),
itemStyle: {
color: upColor,
color0: downColor,
borderColor: upColor,
borderColor0: downColor,
borderWidth: 1
},
emphasis: {
itemStyle: {
borderWidth: 2,
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.3)'
}
},
...getAnimationConfig()
}
],
dataZoom: props.showDataZoom
? [
{
type: 'inside',
start: props.dataZoomStart,
end: props.dataZoomEnd
},
{
show: true,
type: 'slider',
top: '90%',
start: props.dataZoomStart,
end: props.dataZoomEnd
}
]
: undefined
}
}
})
</script>