Skip to content

TimeRangeSelector 时间范围选择 ​

用于在一天内选择一个或多个时间区间,适合排班、报表筛选和监控时间段配置。

基础用法 ​

新建位置 00:00 至 00:30;已选区间:08:00 - 12:00 / 14:00 - 18:00
08:00 - 12:00
14:00 - 18:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
[
  [
    "08:00",
    "12:00"
  ],
  [
    "14:00",
    "18:00"
  ]
]
vue
<template>
  <x-time-range-selector v-model="ranges" />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const ranges = ref<[string, string][]>([
  ['08:00', '12:00'],
  ['14:00', '18:00'],
]);
</script>

禁用时间段 ​

新建位置 00:00 至 00:30;已选区间:07:00 - 11:00 / 15:00 - 19:00
07:00 - 11:00
15:00 - 19:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
[
  [
    "07:00",
    "11:00"
  ],
  [
    "15:00",
    "19:00"
  ]
]
vue
<template>
  <x-time-range-selector
    v-model="ranges"
    :disabled-ranges="[['12:00', '13:00']]"
    :step-minutes="30"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const ranges = ref<[string, string][]>([
  ['07:00', '11:00'],
  ['15:00', '19:00'],
]);
</script>

场景风格 ​

新建位置 00:00 至 00:30;已选区间:07:00 - 11:00
07:00 - 11:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
新建位置 00:00 至 00:30;已选区间:08:30 - 12:30 / 14:00 - 18:00
08:30 - 12:30
14:00 - 18:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
新建位置 00:00 至 00:30;已选区间:00:00 - 04:00 / 19:00 - 23:00
00:00 - 04:00
19:00 - 23:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
新建位置 00:00 至 00:30;已选区间:09:00 - 13:00 / 15:00 - 19:00
09:00 - 13:00
15:00 - 19:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
新建位置 00:00 至 00:30;已选区间:08:00 - 12:00 / 14:00 - 18:00
08:00 - 12:00
14:00 - 18:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

vue
<template>
  <x-time-range-selector
    v-model="defaultRanges"
    scene="default"
    :disabled-ranges="defaultDisabled"
  />
  <x-time-range-selector
    v-model="scheduleRanges"
    scene="schedule"
    :disabled-ranges="scheduleDisabled"
  />
  <x-time-range-selector
    v-model="monitorRanges"
    scene="monitor"
    :disabled-ranges="monitorDisabled"
  />
  <x-time-range-selector
    v-model="compactRanges"
    scene="compact"
    :disabled-ranges="compactDisabled"
  />
  <x-time-range-selector
    v-model="hiddenCursorRanges"
    scene="compact"
    :disabled-ranges="hiddenCursorDisabled"
    :show-cursor="false"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const defaultRanges = ref<[string, string][]>([['07:00', '11:00']]);
const scheduleRanges = ref<[string, string][]>([
  ['08:30', '12:30'],
  ['14:00', '18:00'],
]);
const monitorRanges = ref<[string, string][]>([
  ['00:00', '04:00'],
  ['19:00', '23:00'],
]);
const compactRanges = ref<[string, string][]>([
  ['09:00', '13:00'],
  ['15:00', '19:00'],
]);
const hiddenCursorRanges = ref<[string, string][]>([
  ['08:00', '12:00'],
  ['14:00', '18:00'],
]);

const defaultDisabled = ref<[string, string][]>([['13:00', '15:00']]);
const scheduleDisabled = ref<[string, string][]>([['12:30', '14:00']]);
const monitorDisabled = ref<[string, string][]>([['04:00', '06:00']]);
const compactDisabled = ref<[string, string][]>([['13:00', '14:30']]);
const hiddenCursorDisabled = ref<[string, string][]>([['12:00', '13:00']]);
</script>

自定义颜色 ​

新建位置 00:00 至 00:30;已选区间:08:00 - 12:00
08:00 - 12:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
新建位置 00:00 至 00:30;已选区间:09:00 - 13:00
09:00 - 13:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
新建位置 00:00 至 00:30;已选区间:00:00 - 04:00
00:00 - 04:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
新建位置 00:00 至 00:30;已选区间:15:00 - 19:00
15:00 - 19:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

vue
<template>
  <x-time-range-selector
    v-model="defaultRanges"
    color="#0f766e"
    :disabled-ranges="defaultDisabled"
  />
  <x-time-range-selector
    v-model="scheduleRanges"
    scene="schedule"
    color="#2563eb"
    :disabled-ranges="scheduleDisabled"
  />
  <x-time-range-selector
    v-model="monitorRanges"
    scene="monitor"
    color="#dc2626"
    :disabled-ranges="monitorDisabled"
  />
  <x-time-range-selector
    v-model="compactRanges"
    scene="compact"
    color="#16a34a"
    :disabled-ranges="compactDisabled"
    :show-cursor="false"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const defaultRanges = ref<[string, string][]>([['08:00', '12:00']]);
const scheduleRanges = ref<[string, string][]>([['09:00', '13:00']]);
const monitorRanges = ref<[string, string][]>([['00:00', '04:00']]);
const compactRanges = ref<[string, string][]>([['15:00', '19:00']]);

const defaultDisabled = ref<[string, string][]>([['13:00', '15:00']]);
const scheduleDisabled = ref<[string, string][]>([['13:00', '14:30']]);
const monitorDisabled = ref<[string, string][]>([['04:00', '06:00']]);
const compactDisabled = ref<[string, string][]>([['12:00', '13:30']]);
</script>

更细的主题控制可以覆盖组件 CSS 变量。

vue
<template>
  <x-time-range-selector v-model="ranges" class="custom-time-range" />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const ranges = ref<[string, string][]>([
  ['08:00', '12:00'],
  ['15:00', '19:00'],
]);
</script>

<style scoped>
.custom-time-range {
  --x-time-range-selector-color: #0f766e;
  --x-time-range-selector-grid-radius: 8px;
  --x-time-range-selector-range-height: 34px;
  --x-time-range-selector-range-radius: 8px;
}
</style>

限制范围数量 ​

新建位置 00:00 至 00:30;已选区间:08:00 - 12:00 / 14:00 - 18:00
08:00 - 12:00
14:00 - 18:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
[
  [
    "08:00",
    "12:00"
  ],
  [
    "14:00",
    "18:00"
  ]
]
vue
<template>
  <x-time-range-selector v-model="ranges" :max-ranges="3" />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const ranges = ref<[string, string][]>([
  ['08:00', '12:00'],
  ['14:00', '18:00'],
]);
</script>

允许重叠 ​

新建位置 00:00 至 00:30;已选区间:08:00 - 12:00 / 10:00 - 14:00
08:00 - 12:00
10:00 - 14:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
[
  [
    "08:00",
    "12:00"
  ],
  [
    "10:00",
    "14:00"
  ]
]
vue
<template>
  <x-time-range-selector v-model="ranges" overlap-allowed />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const ranges = ref<[string, string][]>([
  ['08:00', '12:00'],
  ['10:00', '14:00'],
]);
</script>

更细步长 ​

新建位置 00:00 至 00:15;已选区间:08:15 - 12:15
08:15 - 12:15

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
[
  [
    "08:15",
    "12:15"
  ]
]
vue
<template>
  <x-time-range-selector v-model="ranges" :step-minutes="15" />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const ranges = ref<[string, string][]>([
  ['08:15', '12:15'],
]);
</script>

整体禁用与只读 ​

新建位置 00:00 至 00:30;已选区间:08:00 - 12:00
08:00 - 12:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
[ [ "08:00", "12:00" ] ]
vue
<template>
  <x-space><x-checkbox v-model="disabled">禁用</x-checkbox><x-checkbox v-model="readonly">只读</x-checkbox></x-space>
  <x-time-range-selector v-model="ranges" :disabled="disabled" :readonly="readonly" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
const ranges = ref<[string, string][]>([['08:00', '12:00']]);
const disabled = ref(false);
const readonly = ref(false);
</script>

禁用时退出 Tab 序列,只读时仍可聚焦查看。两种状态都阻止创建、移动、缩放与删除;拖拽过程中切换状态会取消预览并恢复外部值。

键盘与删除事件 ​

新建位置 00:00 至 00:30;已选区间:暂无数据

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
Tab 到网格,左右定位,Enter 新建;Tab 到区间,左右移动,Shift 调整结束,Alt 调整开始,Delete 删除。等待键盘或鼠标操作;等待删除
vue
<template>
  <x-time-range-selector v-model="ranges" aria-label="排班时间段" :disabled-ranges="[['12:00', '13:00']]" :max-ranges="3"
    @change="onChange" @remove="onRemove" />
  <p>{{ log }};{{ removed }}</p>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const ranges = ref<[string, string][]>([]);
const log = ref('等待操作');
const removed = ref('等待删除');
const onChange = (value: [string, string][]) => { log.value = `change: ${JSON.stringify(value)}`; };
const onRemove = (value: [string, string]) => { removed.value = `remove: ${value.join(' - ')}`; };
</script>

Home / End 定位到一天的首尾时间块。键盘每次移动一个 stepMinutes,遵守禁区、重叠、最大数量和 24:00 边界。双击区间或在区间聚焦后按 Delete / Backspace 删除。删除先触发 remove,再触发 update:modelValue 和 change。

Form 禁用、校验与重置 ​

新建位置 00:00 至 00:30;已选区间:暂无数据

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
至少选择一个时间段
vue
<template>
  <x-checkbox v-model="disabled">禁用整个表单</x-checkbox>
  <x-form ref="formRef" :model="form" :disabled="disabled" layout="vertical">
    <x-form-item field="ranges" label="工作时段" :rules="{ type: 'array', minLength: 1, message: '至少选择一个时间段' }">
      <x-time-range-selector v-model="form.ranges" />
    </x-form-item>
  </x-form>
  <x-space><x-button @click="validate">校验</x-button><x-button @click="reset">重置</x-button></x-space>
  <p>{{ result }}</p>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import type { FormInstance } from 'x-next';
const form = reactive({ ranges: [] as [string, string][] });
const formRef = ref<FormInstance>();
const disabled = ref(false);
const result = ref('至少选择一个时间段');
const validate = async () => { result.value = (await formRef.value!.validate()) ? '请添加时间段' : '校验通过'; };
const reset = () => { formRef.value!.resetFields(); result.value = '已重置'; };
</script>

用户完成拖拽或键盘修改时触发 Form 的 change 校验;外部模型同步不会重复触发 change。

受控接收与拒绝更新 ​

新建位置 00:00 至 00:30;已选区间:08:00 - 12:00
08:00 - 12:00

网格上用左右方向键定位,Enter 新建;区间上用左右方向键移动,Shift 调整结束,Alt 调整开始,Delete 删除。

0123456789101112131415161718192021222324
当前拒绝修改,操作后会恢复
vue
<template>
  <x-checkbox v-model="accept">接收修改</x-checkbox>
  <x-time-range-selector :model-value="ranges" @update:model-value="handleUpdate" />
  <p>{{ log }}</p>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const ranges = ref<[string, string][]>([['08:00', '12:00']]);
const accept = ref(false);
const log = ref('当前拒绝修改,操作后会恢复');
const handleUpdate = (value: [string, string][]) => {
  log.value = `请求:${JSON.stringify(value)},${accept.value ? '已接收' : '已拒绝'}`;
  if (accept.value) ranges.value = value;
};
</script>

拖拽中显示预览,结束后提交更新请求。父级未同步接收时,下一个渲染周期恢复 modelValue;键盘操作也遵循此规则。外部模型更新会终止正在进行的拖拽。

按需导入 ​

ts
import { TimeRangeSelector } from 'x-next';

样式按需引入(base.css 为共享基础层,多个组件只需引入一次):

ts
import 'x-next/style/base.css';
import 'x-next/style/time-range-selector.css';

Props ​

参数说明类型默认值
modelValue时间范围数组[string, string][]必填
color组件主色,会写入 --x-time-range-selector-color 并派生轨道、选中段和手柄颜色stringundefined
scene场景风格,适配常规筛选、排班预约、监控时段和紧凑筛选'default' | 'schedule' | 'monitor' | 'compact''default'
showCursor是否显示下方 0-24 小时游标booleantrue
overlapAllowed是否允许时间段重叠booleanfalse
stepMinutes时间块步长,单位分钟;非法或不能整除 1440 时降级为 30number30
disabledRanges禁用时间范围[string, string][][]
disabled整体禁用,继承 Form / FormItem 禁用状态booleanfalse
readonly只读,允许聚焦但不允许修改booleanfalse
maxRanges最多可创建范围数量numberInfinity

Events ​

事件名说明回调参数
update:modelValue时间范围提交或约束重新归一化后触发value: [string, string][]
change用户完成修改且值变化时触发,外部同步不触发value: [string, string][]
remove用户删除时间段时触发range: [string, string]

交互说明 ​

  • 在时间轴空白区域按下并拖动可创建范围。
  • 拖动范围主体可移动时间段,拖动过程中会保持原区间宽度。
  • 拖动左右边缘可调整开始和结束时间。
  • 双击已选范围可删除。
  • 当 overlapAllowed 为 false 时,组件会阻止创建或拖入重叠区间。
  • 禁用时间段不会被纳入最终值,拖拽靠近禁用时间段时会停在边界。

disabledRanges 只限制指定时间段;整体禁用请使用 disabled。拖拽时显示内部预览,外部模型更新会终止当前拖拽并同步值。请使用 v-model 接收最终结果;父级拒绝更新时会在下一渲染周期恢复 modelValue。