UNPKG

@fesjs/fes-design

Version:
358 lines (348 loc) 11.1 kB
import { defineComponent, computed, ref, watch, resolveComponent, openBlock, createBlock, withCtx, normalizeClass, normalizeStyle, createSlots, createVNode, createCommentVNode, createElementVNode, withModifiers, createElementBlock, renderSlot, createTextVNode, toDisplayString } from 'vue'; import { UPDATE_MODEL_EVENT } from '../_util/constants'; import useFormAdaptor from '../_util/use/useFormAdaptor'; import getPrefixCls from '../_util/getPrefixCls'; import { useNormalModel } from '../_util/use/useModel'; import { useTheme } from '../_theme/useTheme'; import InputInner from '../input/inputInner.js'; import { ClockCircleOutlined } from '../icon'; import Popper from '../popper'; import FButton from '../button'; import { useLocale } from '../config-provider/useLocale'; import TimeSelect from './time-select.js'; const prefixCls = getPrefixCls('time-picker'); // TODO 支持 12 小时制 function formatTimeCell(data) { return `${data}`.padStart(2, '0'); } const getCurrentTime = format => { const date = new Date(); return [format.indexOf('H') !== -1 && formatTimeCell(date.getHours()), format.indexOf('m') !== -1 && formatTimeCell(date.getMinutes()), format.indexOf('s') !== -1 && formatTimeCell(date.getSeconds())].filter(Boolean).join(':'); }; function validator(val, cellFormat, max) { if (!val) { return false; } if (val.length > 3 || !/^\d{1,2}$/.test(val) || Number(val) > max || val.length < cellFormat.length) { return false; } return true; } function validateTime(data, format) { const times = data.split(':'); const cellFormats = format.split(':'); if (times.length !== cellFormats.length) { return false; } for (let i = 0; i < cellFormats.length; ++i) { const cellFormat = cellFormats[i]; if (/[Hh]/.test(cellFormat)) { if (!validator(times.shift(), cellFormat, 23)) { return false; } } else { if (!validator(times.shift(), cellFormat, 59)) { return false; } } } return true; } const timePickerProps = { modelValue: { // timePicker 目前仅支持string type: String, default: '' }, open: { type: Boolean, default: false }, appendToContainer: { type: Boolean, default: true }, getContainer: { type: Function }, placeholder: { type: String, default: '' }, // FEATURE 下个版本实现 isRange: { type: Boolean, default: false }, disabled: { type: Boolean, default: false }, clearable: { type: Boolean, default: true }, format: { type: String, default: 'HH:mm:ss' }, hourStep: { type: Number, default: 1 }, minuteStep: { type: Number, default: 1 }, secondStep: { type: Number, default: 1 }, disabledHours: Function, disabledMinutes: Function, disabledSeconds: Function, control: { type: Boolean, default: true }, showSuffix: { type: Boolean, default: true }, inputClass: { type: [String, Object, Array] } }; function useOpen(props, emit) { const [isOpened, updateCurrentValue] = useNormalModel(props, emit, { prop: 'open' }); const closePopper = () => { updateCurrentValue(false); }; return { isOpened, closePopper }; } var script = defineComponent({ name: 'FTimePicker', components: { TimeSelect, InputInner, Popper, ClockCircleOutlined, FButton }, props: timePickerProps, emits: [UPDATE_MODEL_EVENT, 'update:open', 'change', 'blur', 'focus'], setup(props, _ref) { let { emit, attrs } = _ref; useTheme(); const { validate, isError, isFormDisabled } = useFormAdaptor({ forbidChildValidate: true }); const innerDisabled = computed(() => props.disabled || isFormDisabled.value); const [currentValue, updateCurrentValue] = useNormalModel(props, emit); const { isOpened, closePopper } = useOpen(props, emit); const classes = computed(() => [prefixCls, (isFormDisabled.value || props.disabled) && 'is-disabled', props.inputClass].filter(Boolean)); const showControl = computed(() => props.control); const showNowShortcut = computed(() => { // format 中的 token 需要与 TimeSelect 中的 canSelectHours 保持一致 if (props.format.includes('H') && props.hourStep !== 1) { return false; } if (props.format.includes('m') && props.minuteStep !== 1) { return false; } if (props.format.includes('s') && props.secondStep !== 1) { return false; } return true; }); // 临时的值 const tempValue = ref(); const { t } = useLocale(); const inputPlaceholder = computed(() => props.placeholder || t('timePicker.placeholder')); const activeTime = ref(); const changeTime = val => { tempValue.value = ''; activeTime.value = val; }; const setCurrentValue = val => { if (val !== currentValue.value) { tempValue.value = ''; updateCurrentValue(val); emit('change', val); validate('change'); activeTime.value = val; } }; // 获取 实例 const timeSelectRef = ref(); // 解耦 与设置值的方法分开 const clear = () => { tempValue.value = ''; updateCurrentValue(''); activeTime.value = ''; emit('change', ''); validate('change'); // 重置时间 timeSelectRef.value.resetTime(); }; watch(isOpened, () => { // 关闭将选中的数据赋值,因此确认按钮只用关闭弹窗即可 if (!isOpened.value && activeTime.value) { setCurrentValue(activeTime.value); } }); const handleInput = val => { tempValue.value = val; if (validateTime(val, props.format)) { setCurrentValue(val); } }; const setCurrentTime = () => { setCurrentValue(getCurrentTime(props.format)); closePopper(); }; const confirmChangeTime = () => { closePopper(); }; // 输入框展示的值 const displayValue = computed(() => { // 目前没有范围选择 if (props.isRange) { return currentValue.value || []; } return tempValue.value || activeTime.value || currentValue.value || ''; }); const handleBlur = event => { closePopper(); emit('blur', event); validate('blur'); // 重置手动输入值 tempValue.value = null; }; return { prefixCls, isError, innerDisabled, classes, displayValue, isOpened, currentValue, tempValue, handleInput, handleBlur, clear, changeTime, showControl, showNowShortcut, setCurrentTime, confirmChangeTime, activeTime, inputPlaceholder, t, attrs, timeSelectRef }; } }); function render(_ctx, _cache, $props, $setup, $data, $options) { const _component_ClockCircleOutlined = resolveComponent("ClockCircleOutlined"); const _component_InputInner = resolveComponent("InputInner"); const _component_TimeSelect = resolveComponent("TimeSelect"); const _component_FButton = resolveComponent("FButton"); const _component_Popper = resolveComponent("Popper"); return openBlock(), createBlock(_component_Popper, { modelValue: _ctx.isOpened, "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => _ctx.isOpened = $event), trigger: "click", placement: "bottom-start", popperClass: `${_ctx.prefixCls}-popper`, disabled: _ctx.innerDisabled, appendToContainer: _ctx.appendToContainer, getContainer: _ctx.getContainer, hideAfter: 0, offset: 4, onlyShowTrigger: "" }, { trigger: withCtx(() => [!_ctx.isRange ? (openBlock(), createBlock(_component_InputInner, { key: 0, class: normalizeClass([_ctx.attrs.class, _ctx.classes]), style: normalizeStyle(_ctx.attrs.style), modelValue: _ctx.displayValue, placeholder: _ctx.inputPlaceholder, disabled: _ctx.innerDisabled, clearable: _ctx.clearable, innerIsError: _ctx.isError, onClear: _ctx.clear, onInput: _ctx.handleInput, onFocus: _cache[0] || (_cache[0] = event => _ctx.$emit('focus', event)), onBlur: _ctx.handleBlur }, createSlots({ _: 2 /* DYNAMIC */ }, [_ctx.showSuffix ? { name: "suffix", fn: withCtx(() => [createVNode(_component_ClockCircleOutlined)]), key: "0" } : undefined]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["class", "style", "modelValue", "placeholder", "disabled", "clearable", "innerIsError", "onClear", "onInput", "onBlur"])) : createCommentVNode("v-if", true)]), default: withCtx(() => [createElementVNode("div", { class: normalizeClass(`${_ctx.prefixCls}-dropdown`), onMousedown: _cache[3] || (_cache[3] = withModifiers(() => {}, ["prevent"])) }, [createVNode(_component_TimeSelect, { ref: "timeSelectRef", visible: _ctx.isOpened, modelValue: _ctx.currentValue, format: _ctx.format, "hour-step": _ctx.hourStep, "minute-step": _ctx.minuteStep, "second-step": _ctx.secondStep, "disabled-hours": _ctx.disabledHours, "disabled-minutes": _ctx.disabledMinutes, "disabled-seconds": _ctx.disabledSeconds, onChange: _ctx.changeTime }, null, 8 /* PROPS */, ["visible", "modelValue", "format", "hour-step", "minute-step", "second-step", "disabled-hours", "disabled-minutes", "disabled-seconds", "onChange"]), _ctx.showControl || _ctx.$slots.addon ? (openBlock(), createElementBlock("div", { key: 0, class: normalizeClass(`${_ctx.prefixCls}-addon`) }, [renderSlot(_ctx.$slots, "addon", { activeTime: _ctx.activeTime }, () => [createElementVNode("div", { class: normalizeClass(`${_ctx.prefixCls}-addon-inner`) }, [_ctx.showNowShortcut ? (openBlock(), createBlock(_component_FButton, { key: 0, type: "link", size: "small", onMousedown: _cache[1] || (_cache[1] = withModifiers(() => {}, ["prevent"])), onClick: _ctx.setCurrentTime }, { default: withCtx(() => [createTextVNode(toDisplayString(_ctx.t('timePicker.now')), 1 /* TEXT */)]), _: 1 /* STABLE */ }, 8 /* PROPS */, ["onClick"])) : createCommentVNode("v-if", true), createVNode(_component_FButton, { type: "primary", size: "small", onMousedown: _cache[2] || (_cache[2] = withModifiers(() => {}, ["prevent"])), onClick: _ctx.confirmChangeTime }, { default: withCtx(() => [createTextVNode(toDisplayString(_ctx.t('timePicker.confirm')), 1 /* TEXT */)]), _: 1 /* STABLE */ }, 8 /* PROPS */, ["onClick"])], 2 /* CLASS */)])], 2 /* CLASS */)) : createCommentVNode("v-if", true)], 34 /* CLASS, NEED_HYDRATION */)]), _: 3 /* FORWARDED */ }, 8 /* PROPS */, ["modelValue", "popperClass", "disabled", "appendToContainer", "getContainer"]); } script.render = render; script.__file = "components/time-picker/time-picker.vue"; export { script as default, timePickerProps };