Appearance
LineChart 折线图
用于展示指标随时间的变化趋势:支持多系列对比、面积填充、堆叠面积、平滑与缺测断点。
完整日期与窄容器
调整宽度或切换长类目名称。首末文字保持在绘图区内,收边后再次检查间距;超长文字以省略号显示,悬停可读完整内容。
vue
<script setup lang="ts">
import { ref } from 'vue';
import { LineChart } 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%' }">
<LineChart :categories="categories" :series="series" />
</div>
</template>何时使用
- 时间轴上比较多个指标的走势(各模型的 Token 消耗、不同环境的请求量)。
- 关注变化速率与拐点,而不是单个时间点的精确读数。
- 需要在线性趋势里叠加一条对照线(如实际值 vs 目标值)。
与其它图表的分工
| 场景 | 选择 |
|---|---|
| 随时间看变化趋势 | LineChart(本页) |
| 按类目比较大小 | BarChart 柱状趋势图 |
| 看占比构成 | PieChart 饼图 |
| 卡片角落的迷你走势 | TrendChart 趋势图 |
基础用法
categories 与每个系列 data 按下标一一对应;类目轴标签会按当前宽度自动抽稀,末位总是保留。
vue
<template>
<x-line-chart
:categories="categories"
:series="[{ name: '请求量', data: values }]"
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 values = ref([820, 932, 901, 934, 1290]);
</script>多系列与平滑
多系列默认叠放(不堆叠),颜色取自内置分类色板;smooth 默认为 true,关闭后是折线段。
vue
<template>
<x-line-chart :categories="categories" :series="series" legend />
</template>
<script setup lang="ts">
import { ref } from 'vue';
const categories = ref(['09-28', '09-29', '09-30', '10-01', '10-02']);
const series = ref([
{ name: '输入 Token', data: [820, 932, 901, 934, 1290] },
{ name: '输出 Token', data: [420, 512, 401, 634, 890] },
]);
</script>面积图与堆叠面积
area 打开填充;配合 stack 变为堆叠面积。堆叠面积适合看「总量 + 构成」——每层填充在下界与上界之间。
vue
<template>
<x-line-chart
:categories="categories"
:series="series"
area
stack
:value-formatter="formatMoney"
legend
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const categories = ref(['09-28', '09-29', '09-30', '10-01', '10-02']);
const series = ref([
{ name: 'deepseek-flash', data: [43.38, 52.4, 38.05, 61.2, 55.8] },
{ name: 'gpt-4o-mini', data: [18.29, 26.1, 12.6, 30.4, 22.1] },
]);
const formatMoney = (value: number) => `¥${value.toFixed(2)}`;
</script>缺测处理
null 表示该时间点没有数据。默认断开折线(避免把「没有调用」画成跌到 0);connectNulls 打开后跨过缺测连到下一点。两者都不会在缺测处产生数据点。
vue
<template>
<x-line-chart
:categories="categories"
:series="[{ name: '工作日请求量', data: values }]"
:connect-nulls="connectNulls"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const connectNulls = ref(false);
const categories = ref(['09-28', '09-29', '09-30', '10-01', '10-02']);
// 周末为 null:默认在该处断开折线
const values = ref([820, null, null, 934, 1290]);
</script>数值轴与负值
正数数据保持 0 基线(不会用非零基线夸大波动);出现负值时才向下扩展出负向刻度,零线会被强调。min / max 可锁定区间。
vue
<template>
<x-line-chart
:categories="categories"
:series="series"
:max="1200"
:grid="{ horizontalLinesNumber: 5 }"
legend
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const categories = ref(['9/1', '9/5', '9/10', '9/15', '9/20', '9/25', '9/30']);
const series = ref([
{ name: 'p50', data: [180, 210, 195, 230, 205, 240, 220] },
{ name: 'p95', data: [420, 680, 510, 940, 720, 1180, 860] },
]);
</script>自定义提示与点击事件
tooltip 插槽可完全接管浮层内容;点击触发 line-click,命中具体数据点时带 seriesIndex 与 value。
尚未点击数据点
vue
<template>
<x-line-chart
:categories="categories"
:series="series"
legend
@line-click="onLineClick"
>
<template #tooltip="{ label, items, formatValue }">
<div style="font-weight: 500">{{ label }}</div>
<div v-for="item in items" :key="item.seriesIndex">
<span>{{ item.name }}</span>
<span>{{ item.value === null ? '-' : formatValue(item.value) }}</span>
</div>
</template>
</x-line-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: [820, 932, 901] },
{ name: '错误数', data: [120, 132, 101] },
]);
const clickLog = ref('尚未点击数据点');
const onLineClick = (data: { label: string; value: number | null }) => {
clickLog.value = `${data.label}:${data.value ?? '缺测'}`;
};
</script>数据点与轴格式化
showPoints 控制数据点:true 常显、false 不显示、'auto'(默认)在点数较少时常显。labels.yFormatter 只影响数值轴,valueFormatter 同时影响提示与图例。
vue
<template>
<x-line-chart
:categories="categories"
:series="series"
:show-points="true"
:smooth="smooth"
:value-formatter="formatCompact"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const smooth = ref(true);
const categories = ref(['09-28', '09-29', '09-30']);
const series = ref([{ name: '请求量', data: [820, 932, 901] }]);
const formatCompact = (value: number) => (value >= 1000 ? `${value / 1000}k` : `${value}`);
</script>加载与空数据
loading 覆盖加载遮罩;全部为 null 或没有系列时显示空态,可用 empty 插槽替换。
vue
<template>
<x-line-chart :categories="categories" :series="series" :loading="loading">
<template #empty>该时段没有调用记录</template>
</x-line-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: [820, 932, 901] }]);
</script>按需导入
ts
import { LineChart } from 'x-next';ts
import { createApp } from 'vue';
import { LineChart } from 'x-next';
import 'x-next/style/base.css';
import 'x-next/style/line-chart.css';
createApp(App).use(LineChart);样式按需引入(base.css 为共享基础层,多个组件只需引入一次):
ts
import 'x-next/style/base.css';
import 'x-next/style/line-chart.css';Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
categories | 类目轴标签,与各系列 data 下标对应 | Array<string | number> | [] |
series | 数据系列,必填 | LineChartSeries[] | [] |
stack | 是否堆叠;面积时变为堆叠面积 | boolean | false |
area | 是否绘制面积填充 | boolean | false |
smooth | 平滑曲线 | boolean | true |
connectNulls | 缺测是否跨过连接 | boolean | false |
showPoints | 数据点显示策略 | boolean | 'auto' | 'auto' |
height | 图表高度,数字按 px | number | string | 280 |
width | 图表宽度,数字按 px | number | string | '100%' |
max | 数值轴上界;传入后锁定刻度 | number | - |
min | 数值轴下界;负值数据自动扩展 | number | - |
padding | 绘制边距,1-4 个非负数字字符串或单个数字 | string | number | '8' |
grid | 网格线配置 | LineChartGridOptions | - |
labels | 轴标签配置 | LineChartLabelsOptions | - |
tooltip | 悬浮提示 | boolean | true |
legend | 图例,点击可隐藏/显示系列 | boolean | false |
valueFormatter | 数值格式化,用于提示、图例与数值轴 | (value: number) => string | 千分位,最多 4 位小数 |
loading | 加载中 | boolean | object | false |
ariaLabel | 无障碍名称;设置后画布带 role="img" | string | - |
LineChartSeries
| 字段 | 说明 | 类型 |
|---|---|---|
name | 系列名,用于提示与图例 | string |
data | 按 categories 顺序排列的值;null 表示缺测 | Array<number | null> |
color | 系列颜色;默认取内置分类色板 | string |
LineChartGridOptions
| 字段 | 说明 | 类型 | 默认值 |
|---|---|---|---|
horizontalLines | 是否显示横向网格线 | boolean | true |
horizontalLinesNumber | 横向网格线数量(含基线) | number | 4 |
LineChartLabelsOptions
| 字段 | 说明 | 类型 | 默认值 |
|---|---|---|---|
xVisible | 是否显示类目标签 | boolean | true |
yVisible | 是否显示数值轴标签 | boolean | true |
xFormatter | 类目标签格式化 | (label: string, index: number) => string | - |
yFormatter | 数值轴刻度格式化(只影响轴) | (value: number) => string | 跟随 valueFormatter |
fontSize | 轴标签字号 | number | 12 |
color | 轴标签颜色 | string | 跟随主题 |
Events
| 事件名 | 说明 | 参数 |
|---|---|---|
lineClick | 点击数据点或绘图区时触发 | (data: LineChartClickData) |
legendChange | 图例切换后触发 | (visibleIndexes: number[]) |
LineChartClickData 包含 index / label / items / total,以及命中数据点的 seriesIndex 与 value(未命中时为 null)。
Slots
| 插槽名 | 说明 | 参数 |
|---|---|---|
tooltip | 自定义悬浮提示内容 | LineChartTooltipSlotProps(含 formatValue) |
legend | 自定义图例 | { series: LineChartSeries[]; hiddenIndexes: number[] } |
point | 自定义数据点渲染 | { seriesIndex, index, value, x, y, color } |
empty | 自定义空态内容 | - |
交互说明
- 数值轴:默认按 1 / 2 / 5 步长取整(刻度始终是
0.5、80这类可读值),因此实际刻度行数可能少于horizontalLinesNumber;传min/max后按锁定值均分。 - 零基线:纯正数数据不会用非零基线;出现负值时才扩展负向刻度。
- 缺测:默认断线,
connectNulls改为跨过;两种模式都不会在缺测处产生数据点,避免鼠标命中不存在的点。 - 堆叠:按可见系列的顺序自下而上累加;缺测按 0 处理,保证堆叠层不出现空洞。命中的系列高亮显示,其余系列由样式淡化。
- x 轴标签:按当前宽度和文字宽度自动抽稀,优先保留末位;收边后再次检查间距。空间不足时省略其他标签,单个超长标签以省略号显示并提供完整文字提示。
- 键盘导航:绘图区可聚焦,左右方向键移动读数、Home / End 跳到首尾、Esc 关闭提示;键盘导航时通过
aria-live播报。 - 性能:整块画布只挂一个 mousemove 与一个提示节点,不为每个数据点创建浮层或监听器;数据点默认在点数较少时常显,长序列自动隐藏。