Skip to content

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 时并排booleanfalse
height图表高度,数字按 pxnumber | string240
width图表宽度,数字按 pxnumber | string'100%'
max数值轴上界;传入后锁定刻度,超出部分截断number-
barWidth柱体宽度,默认按类目带自适应number-
barRadius柱顶圆角半径number4
padding绘制边距,1-4 个非负数字字符串或单个数字string | number'8'
grid网格线配置BarChartGridOptions-
labels轴标签配置BarChartLabelsOptions-
tooltip悬浮提示booleantrue
legend图例,点击可隐藏/显示系列booleanfalse
valueFormatter数值格式化,用于提示、图例与数值轴(value: number) => string千分位,最多 4 位小数
loading加载中boolean | objectfalse
ariaLabel无障碍名称;设置后画布带 role="img"string-

BarChartSeries ​

字段说明类型
name系列名,用于提示与图例string
data按 categories 顺序排列的值;null / 负数按 0 处理Array<number | null>
color系列颜色;默认取内置分类色板string

BarChartGridOptions ​

字段说明类型默认值
horizontalLines是否显示横向网格线booleantrue
horizontalLinesNumber横向网格线数量(含基线)number4

BarChartLabelsOptions ​

字段说明类型默认值
xVisible是否显示类目标签booleantrue
yVisible是否显示数值轴标签booleantrue
xFormatter类目标签格式化(label: string, index: number) => string-
yFormatter数值轴刻度格式化(value: number) => string跟随 valueFormatter
fontSize轴标签字号number12
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 与一个提示节点,不为每个柱体创建浮层或监听器。