Appearance
BarChart 柱状趋势图
用于展示按天(或任意时间粒度)聚合的指标趋势,支持单系列柱状、多系列并排与堆叠,内置悬浮提示、图例联动和键盘导航。
何时使用
- 需要在时间轴上比较各周期的高低,且起点为零(计数、访问量、消费金额这类可加量)。
- 数据按天 / 小时聚合,关注的是分布形态与峰值位置,而不是精确到小数点的读数。
- 单系列看趋势,多系列看结构(并排比较或堆叠看总量与构成)。
与其它图表的分工
| 场景 | 选择 |
|---|---|
| 随时间看变化趋势 | LineChart 折线图 |
| 看占比构成 | PieChart 饼图 |
| 卡片角落的一行迷你走势,不需要坐标轴和读数 | TrendChart 迷你趋势图 |
| 需要坐标轴刻度、悬浮读数、多系列对比或堆叠 | BarChart(本页) |
完整的选择指引见图表总览。
基础用法
categories 与每个系列 data 按下标一一对应,数值上限由组件自动取整到 5、10、15 这类整洁刻度。
vue
<template>
<x-bar-chart
:categories="categories"
:series="[{ name: '浏览量', data: views }]"
aria-label="近 30 天每日浏览量"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const categories = ref(['09-28', '09-29', '09-30', '10-01', '10-02']);
const views = ref([86, 142, 98, 210, 176]);
</script>完整日期与窄容器
调整预览宽度或切换长类目名称。标签会按可用空间抽稀,优先保留末位,并在边缘收边;单个超长标签会省略,悬停省略文字可读完整内容。
vue
<script setup lang="ts">
import { ref } from 'vue';
import { BarChart } from 'x-next';
const width = ref(320);
const categories = Array.from({ length: 30 }, (_, index) =>
new Date(Date.UTC(2026, 8, 6 + index)).toISOString().slice(0, 10),
);
const series = [{ name: '浏览量', data: Array.from({ length: 30 }, (_, index) => 8 + index % 19) }];
</script>
<template>
<label>宽度 <input v-model.number="width" type="range" min="240" max="966" /></label>
<div :style="{ width: `${width}px`, maxWidth: '100%' }">
<BarChart :categories="categories" :series="series" />
</div>
</template>多系列与堆叠
多系列默认并排比较;打开 stack 后自下而上堆叠,提示中会给出各类目合计,适合看「总量 + 构成」。
vue
<template>
<x-bar-chart
:categories="categories"
:series="series"
stack
:value-formatter="formatMoney"
aria-label="近 30 天各用户消费金额"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const categories = ref(['09-28', '09-29', '09-30']);
const series = ref([
{ name: '李守东', data: [43.38, 12.4, 30.05] },
{ name: '唐忠阳', data: [18.29, 26.1, 8.6] },
{ name: '许宇', data: [58.59, 31.2, 24.4] },
]);
</script>图例联动
开启 legend 后点击图例项可临时隐藏某个系列,数值轴会按剩余可见系列重新计算。
vue
<template>
<x-bar-chart
:categories="categories"
:series="series"
legend
@legend-change="onLegendChange"
/>
<p>当前可见系列:{{ visibleIndexes.length }}</p>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const categories = ref(['09-28', '09-29', '09-30']);
const series = ref([
{ name: '李守东', data: [43.38, 12.4, 30.05] },
{ name: '唐忠阳', data: [18.29, 26.1, 8.6] },
]);
const visibleIndexes = ref([0, 1]);
const onLegendChange = (indexes: number[]) => {
visibleIndexes.value = indexes;
};
</script>坐标轴与网格
labels 控制轴标签的显示与格式化,grid 控制横向网格线,max 可以锁定数值轴上界。
vue
<template>
<x-bar-chart
:categories="categories"
:series="[{ name: '请求次数', data: requests }]"
:max="200"
:grid="{ horizontalLinesNumber: 5 }"
:labels="{
xFormatter: (label: string) => label.replace(':00', ''),
yFormatter: (value: number) => `${value}`,
}"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const categories = ref(Array.from({ length: 24 }, (_, i) => `${String(i).padStart(2, '0')}:00`));
const requests = ref([12, 8, 6, 5, 4, 7, 18, 42, 76, 120, 143, 138, 126, 151, 164, 149, 132, 118, 96, 74, 52, 38, 26, 17]);
</script>自定义提示与点击事件
tooltip 插槽可完全接管浮层内容;点击柱体触发 bar-click,未命中柱体(如零值类目)时 seriesIndex 与 value 为 null。
尚未点击柱体
vue
<template>
<x-bar-chart
:categories="categories"
:series="series"
stack
@bar-click="onBarClick"
>
<template #tooltip="{ label, total, items, formatValue }">
<div style="font-weight: 500">{{ label }}</div>
<div v-for="item in items" :key="item.seriesIndex">
<span>{{ item.name }}</span>
<span>{{ formatValue(item.value) }}</span>
</div>
<div>合计 {{ formatValue(total) }}</div>
</template>
</x-bar-chart>
<p>{{ clickLog }}</p>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const categories = ref(['09-28', '09-29', '09-30']);
const series = ref([
{ name: '李守东', data: [43.38, 12.4, 30.05] },
{ name: '唐忠阳', data: [18.29, 26.1, 8.6] },
]);
const clickLog = ref('尚未点击柱体');
const onBarClick = (data: { label: string; total: number; value: number | null }) => {
clickLog.value = `${data.label}:合计 ${data.total}`;
};
</script>数据粒度切换
粒度和主题切换这类外部状态改变时重新传入 categories / series 即可,组件会重新测量并按新宽度抽稀 x 轴标签。
vue
<template>
<x-radio-group v-model="granularity" type="button">
<x-radio value="day">按天</x-radio>
<x-radio value="hour">按小时</x-radio>
</x-radio-group>
<x-bar-chart
:categories="categories"
:series="[{ name: '浏览量', data: values }]"
/>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
const granularity = ref('day');
const dayCategories = ['09-28', '09-29', '09-30'];
const dayValues = [86, 142, 98];
const hourCategories = ['00:00', '01:00', '02:00', '03:00'];
const hourValues = [12, 8, 6, 5];
const categories = computed(() => (granularity.value === 'day' ? dayCategories : hourCategories));
const values = computed(() => (granularity.value === 'day' ? dayValues : hourValues));
</script>自定义颜色与柱形
series[].color 覆盖单系列的取色,bar 插槽可以完全自绘柱体(例如渐变色块)。
vue
<template>
<x-bar-chart
:categories="categories"
:series="[{ name: '浏览量', data: values, color: 'var(--x-color-success-solid)' }]"
:bar-radius="8"
:bar-width="18"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const categories = ref(['09-28', '09-29', '09-30', '10-01']);
const values = ref([86, 142, 98, 210]);
</script>加载与空数据
loading 覆盖加载遮罩;全部为 0 或没有系列时显示空态,可用 empty 插槽替换。
vue
<template>
<x-bar-chart
:categories="categories"
:series="series"
:loading="loading"
>
<template #empty>暂无该时段数据</template>
</x-bar-chart>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const loading = ref(true);
const categories = ref(['09-28', '09-29', '09-30']);
const series = ref([{ name: '浏览量', data: [86, 142, 98] }]);
</script>按需导入
ts
import { BarChart } from 'x-next';ts
import { createApp } from 'vue';
import { BarChart } from 'x-next';
import 'x-next/style/base.css';
import 'x-next/style/bar-chart.css';
createApp(App).use(BarChart);样式按需引入(base.css 为共享基础层,多个组件只需引入一次):
ts
import 'x-next/style/base.css';
import 'x-next/style/bar-chart.css';Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
categories | 类目轴标签,与各系列 data 下标对应 | Array<string | number> | [] |
series | 数据系列,必填 | BarChartSeries[] | [] |
stack | 多系列是否堆叠;false 时并排 | boolean | false |
height | 图表高度,数字按 px | number | string | 240 |
width | 图表宽度,数字按 px | number | string | '100%' |
max | 数值轴上界;传入后锁定刻度,超出部分截断 | number | - |
barWidth | 柱体宽度,默认按类目带自适应 | number | - |
barRadius | 柱顶圆角半径 | number | 4 |
padding | 绘制边距,1-4 个非负数字字符串或单个数字 | string | number | '8' |
grid | 网格线配置 | BarChartGridOptions | - |
labels | 轴标签配置 | BarChartLabelsOptions | - |
tooltip | 悬浮提示 | boolean | true |
legend | 图例,点击可隐藏/显示系列 | boolean | false |
valueFormatter | 数值格式化,用于提示、图例与数值轴 | (value: number) => string | 千分位,最多 4 位小数 |
loading | 加载中 | boolean | object | false |
ariaLabel | 无障碍名称;设置后画布带 role="img" | string | - |
BarChartSeries
| 字段 | 说明 | 类型 |
|---|---|---|
name | 系列名,用于提示与图例 | string |
data | 按 categories 顺序排列的值;null / 负数按 0 处理 | Array<number | null> |
color | 系列颜色;默认取内置分类色板 | string |
BarChartGridOptions
| 字段 | 说明 | 类型 | 默认值 |
|---|---|---|---|
horizontalLines | 是否显示横向网格线 | boolean | true |
horizontalLinesNumber | 横向网格线数量(含基线) | number | 4 |
BarChartLabelsOptions
| 字段 | 说明 | 类型 | 默认值 |
|---|---|---|---|
xVisible | 是否显示类目标签 | boolean | true |
yVisible | 是否显示数值轴标签 | boolean | true |
xFormatter | 类目标签格式化 | (label: string, index: number) => string | - |
yFormatter | 数值轴刻度格式化 | (value: number) => string | 跟随 valueFormatter |
fontSize | 轴标签字号 | number | 12 |
color | 轴标签颜色 | string | 跟随主题 |
Events
| 事件名 | 说明 | 参数 |
|---|---|---|
barClick | 点击柱体或绘图区时触发 | (data: BarChartClickData) |
legendChange | 图例切换后触发 | (visibleIndexes: number[]) |
BarChartClickData 包含 index / label / items / total,以及命中柱体的 seriesIndex 与 value(未命中时为 null)。
Slots
| 插槽名 | 说明 | 参数 |
|---|---|---|
tooltip | 自定义悬浮提示内容 | BarChartTooltipSlotProps(含 formatValue) |
legend | 自定义图例 | { series: BarChartSeries[]; hiddenIndexes: number[] } |
bar | 自定义柱体渲染 | { seriesIndex, index, value, x, y, width, height, color } |
empty | 自定义空态内容 | - |
交互说明
- 数值轴刻度:默认按 1 / 2 / 5 步长取整,刻度值始终是
0.5、80、1,500,000这类可读值;因此实际刻度行数可能少于horizontalLinesNumber。传max后按锁定值均分。 - 缺测与零值:
null、undefined与负数按 0 处理且不渲染柱体(避免出现贴底假柱),全部为 0 时显示空态。 - 堆叠间隙:堆叠分段之间保留 1px 间隙,基线不缩,柱体始终坐落在轴线上。
- x 轴标签:按当前宽度和文字宽度自动抽稀,优先保留末位;收边后再次检查间距。空间不足时省略其他标签,单个超长标签以省略号显示并提供完整文字提示;连省略号也放不下时不绘制标签。
- 键盘导航:绘图区可聚焦,左右方向键移动读数、Home / End 跳到首尾、Esc 关闭提示;键盘导航时通过
aria-live播报,鼠标悬浮不播报。 - 性能:整块画布只挂一个 mousemove 与一个提示节点,不为每个柱体创建浮层或监听器。