@fesjs/fes-design
Version:
fes-design for PC
358 lines (348 loc) • 11.1 kB
JavaScript
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 };