小程序初始提交
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
<template>
|
||||
<view
|
||||
class="cl-input-number"
|
||||
:class="[
|
||||
{
|
||||
'cl-input-number--disabled': isDisabled
|
||||
},
|
||||
pt.className
|
||||
]"
|
||||
>
|
||||
<view
|
||||
class="cl-input-number__minus"
|
||||
:class="[
|
||||
{
|
||||
'is-disabled': !isMinus
|
||||
},
|
||||
pt.op?.className,
|
||||
pt.op?.minus?.className
|
||||
]"
|
||||
hover-class="!bg-surface-200"
|
||||
:hover-stay-time="250"
|
||||
:style="{
|
||||
height: parseRpx(size!),
|
||||
width: parseRpx(size!)
|
||||
}"
|
||||
@touchstart="onMinus"
|
||||
@touchend="longPress.stop"
|
||||
@touchcancel="longPress.stop"
|
||||
>
|
||||
<cl-icon
|
||||
name="subtract-line"
|
||||
:size="pt.op?.icon?.size ?? 36"
|
||||
:color="pt.op?.icon?.color ?? 'info'"
|
||||
:pt="{
|
||||
className: pt.op?.icon?.className
|
||||
}"
|
||||
></cl-icon>
|
||||
</view>
|
||||
|
||||
<view class="cl-input-number__value">
|
||||
<cl-input
|
||||
:model-value="`${value}`"
|
||||
:type="inputType"
|
||||
:disabled="isDisabled"
|
||||
:clearable="false"
|
||||
:readonly="inputable == false"
|
||||
:placeholder="placeholder"
|
||||
:hold-keyboard="false"
|
||||
:pt="{
|
||||
className: `!h-full w-[120rpx] ${pt.value?.className}`,
|
||||
inner: {
|
||||
className: `text-center ${pt.value?.input?.className}`
|
||||
}
|
||||
}"
|
||||
@blur="onBlur"
|
||||
></cl-input>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="cl-input-number__plus"
|
||||
:class="[
|
||||
{
|
||||
'is-disabled': !isPlus
|
||||
},
|
||||
pt.op?.className,
|
||||
pt.op?.plus?.className
|
||||
]"
|
||||
hover-class="!bg-primary-600"
|
||||
:hover-stay-time="250"
|
||||
:style="{
|
||||
height: parseRpx(size!),
|
||||
width: parseRpx(size!)
|
||||
}"
|
||||
@touchstart="onPlus"
|
||||
@touchend="longPress.stop"
|
||||
@touchcancel="longPress.stop"
|
||||
>
|
||||
<cl-icon
|
||||
name="add-line"
|
||||
:size="pt.op?.icon?.size ?? 36"
|
||||
:color="pt.op?.icon?.color ?? 'white'"
|
||||
:pt="{
|
||||
className: pt.op?.icon?.className
|
||||
}"
|
||||
></cl-icon>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, ref, watch, type PropType } from "vue";
|
||||
import type { PassThroughProps } from "../../types";
|
||||
import type { ClIconProps } from "../cl-icon/props";
|
||||
import { useLongPress, parsePt, parseRpx } from "@/cool";
|
||||
import { useForm } from "../../hooks";
|
||||
|
||||
defineOptions({
|
||||
name: "cl-input-number"
|
||||
});
|
||||
|
||||
// 定义组件属性
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 透传样式配置
|
||||
pt: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
// 占位符 - 输入框为空时显示的提示文本
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
// 步进值 - 点击加减按钮时改变的数值
|
||||
step: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
// 最大值 - 允许输入的最大数值
|
||||
max: {
|
||||
type: Number,
|
||||
default: 100
|
||||
},
|
||||
// 最小值 - 允许输入的最小数值
|
||||
min: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 输入框类型 - digit表示带小数点的数字键盘,number表示纯数字键盘
|
||||
inputType: {
|
||||
type: String as PropType<"digit" | "number">,
|
||||
default: "number"
|
||||
},
|
||||
// 是否可输入 - 控制是否允许手动输入数值
|
||||
inputable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否禁用 - 禁用后无法输入和点击加减按钮
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 组件大小 - 控制加减按钮的尺寸,支持数字或字符串形式
|
||||
size: {
|
||||
type: [Number, String] as PropType<number | string>,
|
||||
default: 50
|
||||
}
|
||||
});
|
||||
|
||||
// 定义组件事件
|
||||
const emit = defineEmits(["update:modelValue", "change"]);
|
||||
|
||||
// 长按操作
|
||||
const longPress = useLongPress();
|
||||
|
||||
// cl-form 上下文
|
||||
const { disabled } = useForm();
|
||||
|
||||
// 是否禁用
|
||||
const isDisabled = computed(() => {
|
||||
return disabled.value || props.disabled;
|
||||
});
|
||||
|
||||
// 数值样式
|
||||
type ValuePassThrough = {
|
||||
className?: string;
|
||||
input?: PassThroughProps;
|
||||
};
|
||||
|
||||
// 操作按钮样式
|
||||
type OpPassThrough = {
|
||||
className?: string;
|
||||
minus?: PassThroughProps;
|
||||
plus?: PassThroughProps;
|
||||
icon?: ClIconProps;
|
||||
};
|
||||
|
||||
// 定义透传样式类型
|
||||
type PassThrough = {
|
||||
className?: string;
|
||||
value?: ValuePassThrough;
|
||||
op?: OpPassThrough;
|
||||
};
|
||||
|
||||
// 解析透传样式配置
|
||||
const pt = computed(() => parsePt<PassThrough>(props.pt));
|
||||
|
||||
// 绑定值
|
||||
const value = ref(props.modelValue);
|
||||
|
||||
// 是否可以继续增加数值
|
||||
const isPlus = computed(() => !isDisabled.value && value.value < props.max);
|
||||
|
||||
// 是否可以继续减少数值
|
||||
const isMinus = computed(() => !isDisabled.value && value.value > props.min);
|
||||
|
||||
/**
|
||||
* 更新数值并触发事件
|
||||
* 确保数值在最大值和最小值范围内
|
||||
*/
|
||||
function update() {
|
||||
nextTick(() => {
|
||||
let val = value.value;
|
||||
|
||||
// 处理小于最小值的情况
|
||||
if (val < props.min) {
|
||||
val = props.min;
|
||||
}
|
||||
|
||||
// 处理大于最大值的情况
|
||||
if (val > props.max) {
|
||||
val = props.max;
|
||||
}
|
||||
|
||||
// 处理最小值大于最大值的异常情况
|
||||
if (props.min > props.max) {
|
||||
val = props.max;
|
||||
}
|
||||
|
||||
// 小数点后两位
|
||||
if (props.inputType == "digit") {
|
||||
val = parseFloat(val.toFixed(2));
|
||||
}
|
||||
|
||||
// 更新值,确保值是数字
|
||||
value.value = val;
|
||||
|
||||
// 如果值发生变化,则触发事件
|
||||
if (val != props.modelValue) {
|
||||
emit("update:modelValue", val);
|
||||
emit("change", val);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击加号按钮处理函数 (支持长按)
|
||||
* 在非禁用状态下增加step值
|
||||
*/
|
||||
function onPlus() {
|
||||
if (isDisabled.value || !isPlus.value) return;
|
||||
|
||||
longPress.start(() => {
|
||||
if (isPlus.value) {
|
||||
const val = props.max - value.value;
|
||||
value.value += val > props.step ? props.step : val;
|
||||
update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击减号按钮处理函数 (支持长按)
|
||||
* 在非禁用状态下减少step值
|
||||
*/
|
||||
function onMinus() {
|
||||
if (isDisabled.value || !isMinus.value) return;
|
||||
|
||||
longPress.start(() => {
|
||||
if (isMinus.value) {
|
||||
const val = value.value - props.min;
|
||||
value.value -= val > props.step ? props.step : val;
|
||||
update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入框失去焦点处理函数
|
||||
* @param val 输入的字符串值
|
||||
*/
|
||||
function onBlur(e: UniInputBlurEvent) {
|
||||
if (e.detail.value == "") {
|
||||
value.value = 0;
|
||||
} else {
|
||||
value.value = parseFloat(e.detail.value);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
// 监听绑定值变化
|
||||
watch(
|
||||
computed(() => props.modelValue),
|
||||
(val: number) => {
|
||||
value.value = val;
|
||||
update();
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
// 监听最大值变化,确保当前值不超过新的最大值
|
||||
watch(
|
||||
computed(() => props.max),
|
||||
update
|
||||
);
|
||||
|
||||
// 监听最小值变化,确保当前值不小于新的最小值
|
||||
watch(
|
||||
computed(() => props.min),
|
||||
update
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cl-input-number {
|
||||
@apply flex flex-row items-center;
|
||||
|
||||
&__plus,
|
||||
&__minus {
|
||||
@apply flex items-center justify-center rounded-md bg-surface-100;
|
||||
|
||||
&.is-disabled {
|
||||
@apply opacity-50;
|
||||
}
|
||||
}
|
||||
|
||||
&__plus {
|
||||
@apply bg-primary-500;
|
||||
}
|
||||
|
||||
&__value {
|
||||
@apply flex flex-row items-center justify-center h-full;
|
||||
margin: 0 12rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { PassThroughProps } from "../../types";
|
||||
import type { ClIconProps } from "../cl-icon/props";
|
||||
|
||||
export type ClInputNumberValuePassThrough = {
|
||||
className?: string;
|
||||
input?: PassThroughProps;
|
||||
};
|
||||
|
||||
export type ClInputNumberOpPassThrough = {
|
||||
className?: string;
|
||||
minus?: PassThroughProps;
|
||||
plus?: PassThroughProps;
|
||||
icon?: ClIconProps;
|
||||
};
|
||||
|
||||
export type ClInputNumberPassThrough = {
|
||||
className?: string;
|
||||
value?: ClInputNumberValuePassThrough;
|
||||
op?: ClInputNumberOpPassThrough;
|
||||
};
|
||||
|
||||
export type ClInputNumberProps = {
|
||||
className?: string;
|
||||
modelValue?: number;
|
||||
pt?: ClInputNumberPassThrough;
|
||||
placeholder?: string;
|
||||
step?: number;
|
||||
max?: number;
|
||||
min?: number;
|
||||
inputType?: "digit" | "number";
|
||||
inputable?: boolean;
|
||||
disabled?: boolean;
|
||||
size?: number | string;
|
||||
};
|
||||
Reference in New Issue
Block a user