@cqmcui/cqmcui
Version:
轻量级移动端 Vue2、Vue3 组件库(支持小程序开发)
567 lines (566 loc) • 18.1 kB
JavaScript
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
};