UNPKG

@cqmcui/cqmcui

Version:

轻量级移动端 Vue2、Vue3 组件库(支持小程序开发)

567 lines (566 loc) 18.1 kB
import { reactive, ref, computed, watch, toRefs, onMounted, openBlock, createElementBlock, createElementVNode, normalizeStyle, Fragment, renderList, normalizeClass, toDisplayString, createCommentVNode, resolveComponent, renderSlot, createVNode } from "vue"; import { c as createComponent, d as preventDefault, e as clamp } from "./component-81a4c1d0.js"; import { p as pxCheck } from "./pxCheck-c6b9f6b7.js"; import { u as useTouch } from "./index-7a7385e4.js"; import { _ as _export_sfc } from "./_plugin-vue_export-helper-cc2b3d55.js"; import "../locale/lang"; const { componentName: componentName$1 } = createComponent("picker"); const usePicker = (props, emit) => { const state = reactive({ formattedColumns: props.columns }); let defaultValues = ref([]); const pickerColumn = ref([]); const swipeRef = (el) => { if (el && pickerColumn.value.length < columnsList.value.length) { pickerColumn.value.push(el); } }; const classes = computed(() => { const prefixCls = componentName$1; return { [prefixCls]: true }; }); const selectedOptions = computed(() => { let optins = []; columnsList.value.map((column2, index) => { let currOptions = []; currOptions = column2.filter((item) => item.value == defaultValues.value[index]); optins.push(currOptions[0]); }); return optins; }); const columnsType = computed(() => { const firstColumn = state.formattedColumns[0]; if (firstColumn) { if (Array.isArray(firstColumn)) { return "multiple"; } if ("children" in firstColumn) { return "cascade"; } } return "single"; }); const columnsList = computed(() => { switch (columnsType.value) { case "multiple": return state.formattedColumns; case "cascade": return formatCascade(state.formattedColumns, defaultValues.value ? defaultValues.value : []); default: return [state.formattedColumns]; } }); const formatCascade = (columns, defaultValues2) => { const formatted = []; let cursor = { text: "", value: "", children: columns }; let columnIndex = 0; while (cursor && cursor.children) { const options = cursor.children; const value = defaultValues2[columnIndex]; let index = options.findIndex((columnItem) => columnItem.value == value); if (index == -1) index = 0; cursor = cursor.children[index]; columnIndex++; formatted.push(options); } return formatted; }; const cancel = () => { emit("cancel", { selectedValue: defaultValues.value, selectedOptions: selectedOptions.value }); }; const changeHandler = (columnIndex, option) => { if (option && Object.keys(option).length) { defaultValues.value = defaultValues.value ? defaultValues.value : []; if (columnsType.value === "cascade") { defaultValues.value[columnIndex] = option.value ? option.value : ""; let index = columnIndex; let cursor = option; while (cursor && cursor.children && cursor.children[0]) { defaultValues.value[index + 1] = cursor.children[0].value; index++; cursor = cursor.children[0]; } if (cursor && cursor.children && cursor.children.length == 0) { defaultValues.value = defaultValues.value.slice(0, index + 1); } } else { defaultValues.value[columnIndex] = option.hasOwnProperty("value") ? option.value : ""; } emit("change", { columnIndex, selectedValue: defaultValues.value, selectedOptions: selectedOptions.value }); } }; const confirm = () => { if (defaultValues.value && !defaultValues.value.length) { columnsList.value.forEach((columns) => { defaultValues.value.push(columns[0].value); }); } emit("confirm", { selectedValue: defaultValues.value, selectedOptions: selectedOptions.value }); }; const isSameValue = (valA, valB) => JSON.stringify(valA) === JSON.stringify(valB); watch( () => props.modelValue, (newValues) => { if (!isSameValue(newValues, defaultValues.value)) { defaultValues.value = newValues; } }, { deep: true, immediate: true } ); watch( defaultValues, (newValues) => { if (!isSameValue(newValues, props.modelValue)) { emit("update:modelValue", newValues); } }, { deep: true } ); watch( () => props.columns, (val) => { if (val.length) state.formattedColumns = val; } ); return { classes, ...toRefs(state), columnsType, columnsList, cancel, changeHandler, confirm, defaultValues, pickerColumn, swipeRef, selectedOptions, isSameValue }; }; const { create: create$1 } = createComponent("picker-column"); const _sfc_main$1 = create$1({ props: { // 当前选中项 value: [String, Number], columnsType: String, column: { type: Array, default: () => [] }, // 是否开启3D效果 threeDimensional: { type: Boolean, default: true }, swipeDuration: { type: [Number, String], default: 1e3 }, visibleOptionNum: { type: [Number, String], default: 7 }, optionHeight: { type: [Number, String], default: 36 } }, emits: ["click", "change"], setup(props, { emit }) { const touch = useTouch(); const state = reactive({ touchParams: { startY: 0, endY: 0, startTime: 0, endTime: 0, lastY: 0, lastTime: 0 }, currIndex: 1, transformY: 0, scrollDistance: 0, rotation: 20 }); const roller = ref(null); const moving = ref(false); const touchDeg = ref(0); const touchTime = ref(0); const DEFAULT_DURATION = 200; const INERTIA_TIME = 300; const INERTIA_DISTANCE = 15; const touchRollerStyle = computed(() => { return { transition: `transform ${touchTime.value}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, transform: `rotate3d(1, 0, 0, ${touchDeg.value})`, top: `calc(50% - ${+props.optionHeight / 2}px)` }; }); const touchTileStyle = computed(() => { const { optionHeight } = props; return { transition: `transform ${touchTime.value}ms cubic-bezier(0.17, 0.89, 0.45, 1)`, transform: `translate3d(0, ${state.scrollDistance}px, 0)`, top: `calc(50% - ${+optionHeight / 2}px)`, height: `${optionHeight}px` }; }); const setRollerStyle = (index) => { return `transform: rotate3d(1, 0, 0, ${-state.rotation * index}deg) translate3d(0px, 0px, 104px)`; }; const maskStyles = computed(() => { return { backgroundSize: `100% ${(+props.visibleOptionNum - 1) * +props.optionHeight / 2}px` }; }); const onTouchStart = (event) => { touch.start(event); if (moving.value) { let dom = roller.value; const { transform } = window.getComputedStyle(dom); if (props.threeDimensional) { const circle = Math.floor(parseInt(touchDeg.value) / 360); const cos = +transform.split(", ")[5]; const sin = +transform.split(", ")[6] < 0 ? 180 : 0; const endDeg = circle * 360 + Math.acos(cos) / Math.PI * 180 + sin; state.scrollDistance = -Math.abs((endDeg / state.rotation - 1) * +props.optionHeight); } else { state.scrollDistance = +transform.slice(7, transform.length - 1).split(", ")[5]; } } preventDefault(event, true); state.touchParams.startY = touch.deltaY.value; state.touchParams.startTime = Date.now(); state.transformY = state.scrollDistance; }; const onTouchMove = (event) => { touch.move(event); if (touch.isVertical()) { moving.value = true; preventDefault(event, true); } state.touchParams.lastY = touch.deltaY.value; let move = state.touchParams.lastY - state.touchParams.startY; setMove(move); }; const onTouchEnd = () => { state.touchParams.lastY = touch.deltaY.value; state.touchParams.lastTime = Date.now(); let move = state.touchParams.lastY - state.touchParams.startY; let moveTime = state.touchParams.lastTime - state.touchParams.startTime; if (moveTime <= INERTIA_TIME && Math.abs(move) > INERTIA_DISTANCE) { const distance = momentum(move, moveTime); setMove(distance, "end", +props.swipeDuration); return; } else { setMove(move, "end"); } setTimeout(() => { touch.reset(); moving.value = false; }, 0); }; const momentum = (distance, duration) => { const speed = Math.abs(distance / duration); distance = speed / 3e-3 * (distance < 0 ? -1 : 1); return distance; }; const isHidden = (index) => { if (index >= state.currIndex + 8 || index <= state.currIndex - 8) { return true; } else { return false; } }; const setTransform = (translateY = 0, type, time = DEFAULT_DURATION, deg) => { if (type === "end") { touchTime.value = time; } else { touchTime.value = 0; } touchDeg.value = deg; state.scrollDistance = translateY; }; const setMove = (move, type, time) => { const { optionHeight } = props; let updateMove = move + state.transformY; if (type === "end") { if (updateMove > 0) { updateMove = 0; } if (updateMove < -(props.column.length - 1) * +optionHeight) { updateMove = -(props.column.length - 1) * +optionHeight; } let endMove = Math.round(updateMove / +optionHeight) * +optionHeight; let deg = `${(Math.abs(Math.round(endMove / +optionHeight)) + 1) * state.rotation}deg`; setTransform(endMove, type, time, deg); state.currIndex = Math.abs(Math.round(endMove / +optionHeight)) + 1; } else { let deg = 0; let currentDeg = (-updateMove / +optionHeight + 1) * state.rotation; const maxDeg = (props.column.length + 1) * state.rotation; const minDeg = 0; deg = clamp(currentDeg, minDeg, maxDeg); if (minDeg < deg && deg < maxDeg) { setTransform(updateMove, null, void 0, deg + "deg"); state.currIndex = Math.abs(Math.round(updateMove / +optionHeight)) + 1; } } }; const setChooseValue = () => { emit("change", props.column[state.currIndex - 1]); }; const modifyStatus = (type) => { const { column: column2 } = props; let index = column2.findIndex((columnItem) => columnItem.value == props.value); state.currIndex = index === -1 ? 1 : index + 1; let move = index === -1 ? 0 : index * +props.optionHeight; type && setChooseValue(); setMove(-move); }; const stopMomentum = () => { moving.value = false; touchTime.value = 0; setChooseValue(); }; watch( () => props.column, (val) => { if (props.column && props.column.length > 0) { state.transformY = 0; modifyStatus(false); } }, { deep: true } ); watch( () => props.value, (val) => { state.transformY = 0; modifyStatus(false); }, { deep: true } ); onMounted(() => { modifyStatus(true); }); return { ...toRefs(state), ...toRefs(props), setRollerStyle, isHidden, roller, onTouchStart, onTouchMove, onTouchEnd, touchRollerStyle, touchTileStyle, setMove, stopMomentum, pxCheck, maskStyles }; } }); function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createElementBlock("view", { class: "cqmc-picker__list", onTouchstart: _cache[1] || (_cache[1] = (...args) => _ctx.onTouchStart && _ctx.onTouchStart(...args)), onTouchmove: _cache[2] || (_cache[2] = (...args) => _ctx.onTouchMove && _ctx.onTouchMove(...args)), onTouchend: _cache[3] || (_cache[3] = (...args) => _ctx.onTouchEnd && _ctx.onTouchEnd(...args)) }, [ createElementVNode("view", { class: "cqmc-picker-roller", ref: "roller", style: normalizeStyle(_ctx.threeDimensional ? _ctx.touchRollerStyle : _ctx.touchTileStyle), onTransitionend: _cache[0] || (_cache[0] = (...args) => _ctx.stopMomentum && _ctx.stopMomentum(...args)) }, [ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.column, (item, index) => { return openBlock(), createElementBlock(Fragment, { key: item.value ? item.value : index }, [ item && item.text && _ctx.threeDimensional ? (openBlock(), createElementBlock("view", { key: 0, class: normalizeClass(["cqmc-picker-roller-item", { "cqmc-picker-roller-item-hidden": _ctx.isHidden(index + 1) }]), style: normalizeStyle(_ctx.setRollerStyle(index + 1)) }, toDisplayString(item.text), 7)) : createCommentVNode("", true), item && item.text && !_ctx.threeDimensional ? (openBlock(), createElementBlock("view", { key: 1, class: "cqmc-picker-roller-item-tile", style: normalizeStyle({ height: _ctx.pxCheck(_ctx.optionHeight) }) }, toDisplayString(item.text), 5)) : createCommentVNode("", true) ], 64); }), 128)) ], 36), createElementVNode("view", { class: "cqmc-picker-roller-mask", style: normalizeStyle(_ctx.maskStyles) }, null, 4) ], 32); } const column = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render$1]]); const baseProps = { modelValue: { type: Array, default: () => [] }, title: { type: String, default: "" }, cancelText: { type: String, default: "" }, okText: { type: String, default: "" }, columns: { type: Array, default: () => { return []; } }, threeDimensional: { type: Boolean, default: true }, swipeDuration: { type: [Number, String], default: 1e3 }, showToolbar: { type: Boolean, default: true }, visibleOptionNum: { type: [Number, String], default: 7 }, optionHeight: { type: [Number, String], default: 36 } }; const { componentName, create, translate } = createComponent("picker"); const _sfc_main = create({ components: { [column.name]: column }, props: baseProps, emits: ["cancel", "change", "confirm", "update:modelValue"], setup(props, { emit }) { const { changeHandler, confirm, defaultValues, columnsList, columnsType, classes, cancel } = usePicker(props, emit); const pickerColumn = ref([]); const swipeRef = (el) => { if (el && pickerColumn.value.length < columnsList.value.length) { pickerColumn.value.push(el); } }; const columnStyle = computed(() => { const styles = {}; styles.height = `${+props.visibleOptionNum * +props.optionHeight}px`; styles["--lineHeight"] = `${+props.optionHeight}px`; return styles; }); const confirmHandler = () => { pickerColumn.value.length > 0 && pickerColumn.value.forEach((column2) => { column2.stopMomentum(); }); confirm(); }; return { classes, column, columnsType, columnsList, cancel, changeHandler, confirmHandler, defaultValues, translate, pickerColumn, swipeRef, columnStyle }; } }); const _hoisted_1 = { key: 0, class: "cqmc-picker__bar" }; const _hoisted_2 = { class: "cqmc-picker__title" }; function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { const _component_nut_picker_column = resolveComponent("cqmc-picker-column"); return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.classes) }, [ _ctx.showToolbar ? (openBlock(), createElementBlock("view", _hoisted_1, [ createElementVNode("view", { class: "cqmc-picker__left", onClick: _cache[0] || (_cache[0] = (...args) => _ctx.cancel && _ctx.cancel(...args)) }, toDisplayString(_ctx.cancelText || _ctx.translate("cancel")), 1), createElementVNode("view", _hoisted_2, toDisplayString(_ctx.title), 1), createElementVNode("view", { class: "cqmc-picker__right", onClick: _cache[1] || (_cache[1] = ($event) => _ctx.confirmHandler()) }, toDisplayString(_ctx.okText || _ctx.translate("confirm")), 1) ])) : createCommentVNode("", true), renderSlot(_ctx.$slots, "top"), createElementVNode("view", { class: "cqmc-picker__column", style: normalizeStyle(_ctx.columnStyle) }, [ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.columnsList, (column2, columnIndex) => { return openBlock(), createElementBlock("view", { class: "cqmc-picker__columnitem", key: columnIndex }, [ createVNode(_component_nut_picker_column, { ref_for: true, ref: _ctx.swipeRef, column: column2, columnsType: _ctx.columnsType, value: _ctx.defaultValues && _ctx.defaultValues[columnIndex], threeDimensional: _ctx.threeDimensional, swipeDuration: _ctx.swipeDuration, visibleOptionNum: _ctx.visibleOptionNum, optionHeight: _ctx.optionHeight, onChange: (option) => { _ctx.changeHandler(columnIndex, option); } }, null, 8, ["column", "columnsType", "value", "threeDimensional", "swipeDuration", "visibleOptionNum", "optionHeight", "onChange"]) ]); }), 128)) ], 4), renderSlot(_ctx.$slots, "default") ], 2); } const Picker = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]); export { Picker as default };