feat(chart): support per-track legend positions

This commit is contained in:
李启源
2026-07-22 19:53:00 +08:00
parent 5b0ba7413e
commit c258c11795
6 changed files with 110 additions and 49 deletions

View File

@@ -1079,6 +1079,61 @@ describe('WaveformChart', () => {
expect(wrapper.attributes('data-chart-left-margin')).toBe('64')
})
it('resolves legend positions by stable track id across pages', async () => {
const wrapper = await mountSizedChart(
{
kind: 'series',
series: Array.from({ length: 8 }, (_, index) => ({
id: `series-${index}`,
trackId: `frame-${Math.floor(index / 2)}`,
name: `series ${index}`,
data: {
kind: 'points' as const,
points: [
{ x: 0, y: index },
{ x: 1, y: index + 1 },
],
},
})),
},
{
grid: { rowCount: 2, columnCount: 1, showPagination: true },
legend: {
position: 'left',
orientation: 'auto',
trackPositions: {
'frame-0': 'top',
'frame-1': 'bottom-right',
'frame-2': 'bottom',
},
},
},
)
const legendForTrack = (trackId: string) =>
wrapper.get(`[data-legend-track-id="${trackId}"] .waveform-chart__legend`)
expect(legendForTrack('frame-0').attributes('data-position')).toBe('top')
expect(legendForTrack('frame-0').attributes('data-orientation')).toBe('horizontal')
expect(legendForTrack('frame-1').attributes('data-position')).toBe('bottom-right')
expect(legendForTrack('frame-1').attributes('data-orientation')).toBe('vertical')
await wrapper.setProps({
legend: {
position: 'left',
orientation: 'vertical',
trackPositions: { 'frame-0': 'top', 'frame-1': 'bottom-right', 'frame-2': 'bottom' },
},
})
expect(legendForTrack('frame-0').attributes('data-orientation')).toBe('vertical')
await wrapper.get('.ant-pagination-next button').trigger('click')
expect(legendForTrack('frame-2').attributes('data-position')).toBe('bottom')
expect(legendForTrack('frame-2').attributes('data-orientation')).toBe('vertical')
expect(legendForTrack('frame-3').attributes('data-position')).toBe('left')
expect(legendForTrack('frame-3').attributes('data-orientation')).toBe('vertical')
})
it('applies a configurable alpha background to every visible legend', async () => {
const wrapper = await mountSizedChart(
{

View File

@@ -290,7 +290,6 @@ const containerStyle = computed(() => ({
width: fixedWidth.value === undefined ? '100%' : `${fixedWidth.value}px`,
height: fixedHeight.value === undefined ? '100%' : `${fixedHeight.value}px`,
}))
const legendPosition = computed<WaveformLegendPosition>(() => props.legend?.position ?? 'top-right')
const isCleanView = computed(() => props.cleanView === true)
const resolvedZeroLine = computed(() => {
const width = props.zeroLine?.width
@@ -311,13 +310,17 @@ const hiddenSeriesIdSet = computed(() =>
: new Set(props.hiddenSeriesIds),
)
const resolvedHiddenSeriesIds = computed(() => Array.from(hiddenSeriesIdSet.value))
const legendOrientation = computed<Exclude<WaveformLegendOrientation, 'auto'>>(() => {
function resolveLegendPosition(trackId: string): WaveformLegendPosition {
return props.legend?.trackPositions?.[trackId] ?? props.legend?.position ?? 'top-right'
}
function resolveLegendOrientation(
position: WaveformLegendPosition,
): Exclude<WaveformLegendOrientation, 'auto'> {
const orientation = props.legend?.orientation ?? 'auto'
if (orientation !== 'auto') return orientation
return legendPosition.value === 'top' || legendPosition.value === 'bottom'
? 'horizontal'
: 'vertical'
})
return position === 'top' || position === 'bottom' ? 'horizontal' : 'vertical'
}
const resolvedTitleText = computed(() => props.title?.text.trim() ?? '')
const titleAreaReserved = computed(
() =>
@@ -1966,13 +1969,14 @@ onBeforeUnmount(() => {
:key="`legend-${track.index}-${track.series.name}`"
class="waveform-chart__legend-track"
:data-legend-track-index="track.index"
:data-legend-track-id="track.id"
:transform="`translate(${track.left}, ${track.top})`"
>
<WaveformLegend
v-if="!track.isEmpty && track.legendSeries.length > 1"
:series="track.legendSeries"
:position="legendPosition"
:orientation="legendOrientation"
:position="resolveLegendPosition(track.id)"
:orientation="resolveLegendOrientation(resolveLegendPosition(track.id))"
:background-color="legendBackgroundColor"
:interactive="legendInteractive"
:hidden-series-ids="resolvedHiddenSeriesIds"

View File

@@ -371,6 +371,7 @@ export function buildTrackLayouts(options: BuildTrackLayoutsOptions): TrackLayou
})
return {
id: displayTrack.id,
index,
series,
seriesList: displayTrack.visibleSeries,

View File

@@ -72,6 +72,8 @@ export interface HoveredSeriesPoint extends DisplaySeries {
* 轨道布局
*/
export interface TrackLayout {
/** Stable track key derived from trackId, or from the series id when trackId is omitted. */
id: string
index: number
series: DisplaySeries
/** Visible series used by rendering and interaction code. */