更新记录

1.0.2(2025-07-06) 下载此版本

优化说明文挡

1.0.1(2025-07-06) 下载此版本

优化说明文挡

1.0.0(2025-07-06) 下载此版本

新增组件

查看更多

平台兼容性

uni-app

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
- - - - - - -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 快应用-华为 快应用-联盟
- - - - - - - - - -

hbxw-stepper步进器组件

简介

hbxw-stepper 是一个功能强大的步进器组件,支持数字输入、增减按钮操作,适用于购物车、数量选择等场景。组件提供了丰富的自定义选项和禁用控制功能。

特性

  • 🚀 兼容 Vue2 和 Vue3
  • 🔧 支持自定义最小值、最大值和步长
  • 📊 支持小数步长,精确的数值处理
  • 🎯 双向数据绑定,完整的事件监听
  • 💡 输入验证和边界处理
  • 🎛️ 支持插槽自定义按钮和输入框
  • 🚫 左右按钮和输入框禁用控制
  • 🎨 自定义禁用和启用样式
  • 📱 响应式设计,支持多种场景
  • 🔄 智能事件通知机制

1. 禁用控制

  • 智能禁用:左右按钮会自动根据数值边界进行禁用
  • 手动禁用:支持通过 props 手动禁用任一组件
  • 最终禁用状态:手动禁用 OR 自动禁用
  • 样式响应:禁用状态会应用相应的样式

2. 样式自定义

  • 多种格式:支持 Object 和 String 两种样式格式
  • 样式合并:Object 格式会与默认样式合并
  • 动态更新:样式属性支持响应式更新
  • 状态区分:禁用和启用状态可以设置不同的样式

3. 精度处理

  • 小数支持:完美支持小数步长
  • 精度控制:自动根据步长确定小数位数
  • 数值安全:避免 JavaScript 浮点数精度问题

4. 事件通知

  • 全局事件:支持通过 uni.$emit 进行外部控制
  • 实例隔离:通过 instanceId 区分不同实例
  • 事件类型:支持 decrease、increase、input、input-blur 事件

使用示例

推荐先直接复制示例代码到工程中看效果了解下使用方法再投入项目使用。

<template>
    <view class="demo-container">

        <view class="demo-section">
            <text class="section-title">基础用法</text>
            <view class="demo-item">
                <text class="demo-label">默认步进器:</text>
                <hbxw-stepper v-model="value1" @change="handleChange1"></hbxw-stepper>
                <text class="demo-value">当前值: {{ value1 }}</text>
            </view>
        </view>

        <view class="demo-section">
            <text class="section-title">设置最小值和最大值</text>
            <view class="demo-item">
                <text class="demo-label">范围 0-10:</text>
                <hbxw-stepper v-model="value2" :min="0" :max="10" @change="handleChange2"></hbxw-stepper>
                <text class="demo-value">当前值: {{ value2 }}</text>
            </view>
        </view>

        <view class="demo-section">
            <text class="section-title">自定义步长</text>
            <view class="demo-item">
                <text class="demo-label">步长为5:</text>
                <hbxw-stepper v-model="value3" :step="5" :min="0" :max="100" @change="handleChange3"></hbxw-stepper>
                <text class="demo-value">当前值: {{ value3 }}</text>
            </view>
        </view>

        <view class="demo-section">
            <text class="section-title">小数步长</text>
            <view class="demo-item">
                <text class="demo-label">步长为0.1:</text>
                <hbxw-stepper v-model="value4" :step="0.1" :min="0" :max="10" @change="handleChange4"></hbxw-stepper>
                <text class="demo-value">当前值: {{ value4 }}</text>
            </view>
        </view>

        <view class="demo-section">
            <text class="section-title">大数值范围</text>
            <view class="demo-item">
                <text class="demo-label">范围 100-9999:</text>
                <hbxw-stepper v-model="value5" :min="100" :max="9999" @change="handleChange5"></hbxw-stepper>
                <text class="demo-value">当前值: {{ value5 }}</text>
            </view>
        </view>

        <view class="demo-section">
            <text class="section-title">事件监听</text>
            <view class="demo-item">
                <text class="demo-label">监听变化:</text>
                <hbxw-stepper v-model="value6" @change="handleChange6"></hbxw-stepper>
                <text class="demo-value">当前值: {{ value6 }}</text>
            </view>
            <view class="change-log">
                <text class="log-title">变化日志:</text>
                <view class="log-item" v-for="(log, index) in changeLogs" :key="index">
                    <text class="log-text">{{ log }}</text>
                </view>
            </view>
        </view>

        <view class="demo-section">
            <text class="section-title">禁用控制和自定义样式</text>
            <view class="demo-item">
                <text class="demo-label">左按钮禁用:</text>
                <hbxw-stepper v-model="disableLeftValue" :min="1" :max="10" :leftDisabled="true" @change="handleDisableLeftChange"></hbxw-stepper>
                <text class="demo-value">当前值: {{ disableLeftValue }}</text>
            </view>

            <view class="demo-item">
                <text class="demo-label">右按钮禁用:</text>
                <hbxw-stepper v-model="disableRightValue" :min="1" :max="10" :rightDisabled="true" @change="handleDisableRightChange"></hbxw-stepper>
                <text class="demo-value">当前值: {{ disableRightValue }}</text>
            </view>

            <view class="demo-item">
                <text class="demo-label">输入框禁用:</text>
                <hbxw-stepper v-model="disableInputValue" :min="1" :max="10" :inputDisabled="true" @change="handleDisableInputChange"></hbxw-stepper>
                <text class="demo-value">当前值: {{ disableInputValue }}</text>
            </view>

            <view class="demo-item">
                <text class="demo-label">动态禁用控制:</text>
                <hbxw-stepper 
                    v-model="dynamicDisableValue" 
                    :min="1" 
                    :max="10" 
                    :leftDisabled="dynamicLeftDisabled"
                    :rightDisabled="dynamicRightDisabled"
                    :inputDisabled="dynamicInputDisabled"
                    @change="handleDynamicDisableChange"
                ></hbxw-stepper>
                <text class="demo-value">当前值: {{ dynamicDisableValue }}</text>
            </view>

            <view class="control-buttons">
                <button 
                    class="control-btn" 
                    :class="{ active: dynamicLeftDisabled }"
                    @click="toggleLeftDisabled"
                >
                    {{ dynamicLeftDisabled ? '启用左按钮' : '禁用左按钮' }}
                </button>
                <button 
                    class="control-btn" 
                    :class="{ active: dynamicRightDisabled }"
                    @click="toggleRightDisabled"
                >
                    {{ dynamicRightDisabled ? '启用右按钮' : '禁用右按钮' }}
                </button>
                <button 
                    class="control-btn" 
                    :class="{ active: dynamicInputDisabled }"
                    @click="toggleInputDisabled"
                >
                    {{ dynamicInputDisabled ? '启用输入框' : '禁用输入框' }}
                </button>
            </view>

            <view class="demo-item">
                <text class="demo-label">自定义禁用样式:</text>
                <hbxw-stepper 
                    v-model="customDisableStyleValue" 
                    :min="1" 
                    :max="10" 
                    :leftDisabled="customStyleDisabled"
                    :rightDisabled="customStyleDisabled"
                    :inputDisabled="customStyleDisabled"
                    :disabledLeftStyle="customDisabledLeftStyle"
                    :disabledRightStyle="customDisabledRightStyle"
                    :disabledInputStyle="customDisabledInputStyle"
                    @change="handleCustomDisableStyleChange"
                ></hbxw-stepper>
                <text class="demo-value">当前值: {{ customDisableStyleValue }}</text>
            </view>

            <view class="demo-item">
                <text class="demo-label">自定义启用样式:</text>
                <hbxw-stepper 
                    v-model="customEnableStyleValue" 
                    :min="1" 
                    :max="10" 
                    :enabledLeftStyle="customEnabledLeftStyle"
                    :enabledRightStyle="customEnabledRightStyle"
                    :enabledInputStyle="customEnabledInputStyle"
                    @change="handleCustomEnableStyleChange"
                ></hbxw-stepper>
                <text class="demo-value">当前值: {{ customEnableStyleValue }}</text>
            </view>

            <view class="control-buttons">
                <button 
                    class="control-btn style-btn" 
                    :class="{ active: customStyleDisabled }"
                    @click="toggleCustomStyleDisabled"
                >
                    {{ customStyleDisabled ? '启用自定义样式组件' : '禁用自定义样式组件' }}
                </button>
            </view>
        </view>

        <view class="demo-section">
            <text class="section-title">购物车场景</text>
            <view class="demo-item">
                <text class="demo-label">商品A:</text>
                <hbxw-stepper v-model="cartItem1" :min="0" :max="99" @change="handleCartChange1"></hbxw-stepper>
                <text class="demo-value">数量: {{ cartItem1 }}</text>
            </view>
            <view class="demo-item">
                <text class="demo-label">商品B:</text>
                <hbxw-stepper v-model="cartItem2" :min="0" :max="99" @change="handleCartChange2"></hbxw-stepper>
                <text class="demo-value">数量: {{ cartItem2 }}</text>
            </view>
            <view class="cart-total">
                <text class="total-text">总计: {{ totalItems }} 件商品</text>
            </view>
        </view>

        <view class="demo-section">
            <text class="section-title">自定义插槽示例</text>

            <view class="demo-item">
                <text class="demo-label">圆形按钮(范围1-20):</text>
                <hbxw-stepper 
                    ref="customStepper1"
                    v-model="customValue1" 
                    :min="1" 
                    :max="20" 
                    :step="1"
                    instanceId="custom-stepper-1"
                    @change="handleCustomChange1"
                >
                    <template #left-button="{ disabled }">
                        <view 
                            class="custom-button custom-button-circle" 
                            :class="{ 'custom-button-disabled': disabled }"
                            @click="handleCustomDecrease1"
                        >
                            <text class="custom-button-text">-</text>
                        </view>
                    </template>
                    <template #right-button="{ disabled }">
                        <view 
                            class="custom-button custom-button-circle" 
                            :class="{ 'custom-button-disabled': disabled }"
                            @click="handleCustomIncrease1"
                        >
                            <text class="custom-button-text">+</text>
                        </view>
                    </template>
                </hbxw-stepper>
                <text class="demo-value">当前值: {{ customValue1 }}</text>
            </view>

            <view class="demo-item">
                <text class="demo-label">图标按钮(范围10-50):</text>
                <hbxw-stepper 
                    ref="customStepper2"
                    v-model="customValue2" 
                    :min="10" 
                    :max="50" 
                    :step="2"
                    instanceId="custom-stepper-2"
                    @change="handleCustomChange2"
                >
                    <template #left-button="{ disabled }">
                        <view 
                            class="custom-button custom-icon-button" 
                            :class="{ 'custom-button-disabled': disabled }"
                            @click="handleCustomDecrease2"
                        >
                            <text class="custom-icon">⬇</text>
                        </view>
                    </template>
                    <template #right-button="{ disabled }">
                        <view 
                            class="custom-button custom-icon-button" 
                            :class="{ 'custom-button-disabled': disabled }"
                            @click="handleCustomIncrease2"
                        >
                            <text class="custom-icon">⬆</text>
                        </view>
                    </template>
                </hbxw-stepper>
                <text class="demo-value">当前值: {{ customValue2 }}</text>
            </view>

            <view class="demo-item">
                <text class="demo-label">自定义输入框(范围0-100):</text>
                <hbxw-stepper 
                    ref="customStepper3"
                    v-model="customValue3" 
                    :min="0" 
                    :max="100" 
                    :step="5"
                    instanceId="custom-stepper-3"
                    @change="handleCustomChange3"
                >
                    <template #input="{ value, min, max }">
                        <input
                            :value="value"
                            type="number"
                            class="custom-input"
                            :min="min"
                            :max="max"
                            @input="handleCustomInput3"
                            @blur="handleCustomBlur3"
                            placeholder="请输入数量"
                        />
                    </template>
                </hbxw-stepper>
                <text class="demo-value">当前值: {{ customValue3 }}</text>
            </view>

            <view class="demo-item">
                <text class="demo-label">事件通知控制(范围1-99):</text>
                <hbxw-stepper 
                    ref="eventStepper"
                    v-model="eventValue" 
                    :min="1" 
                    :max="99" 
                    :step="1"
                    instanceId="event-stepper-demo"
                    @change="handleEventChange"
                >
                    <template #left-button="{ instanceId, disabled }">
                        <view 
                            class="event-button event-button-decrease" 
                            :class="{ 'event-button-disabled': disabled }"
                            @click="handleEventDecrease(instanceId)"
                        >
                            <text class="event-button-text">➖</text>
                        </view>
                    </template>
                    <template #input="{ instanceId, value, min, max }">
                        <view class="event-input-container">
                            <input
                                :value="value"
                                type="number"
                                class="event-input"
                                :min="min"
                                :max="max"
                                @input="handleEventInput(instanceId, $event)"
                                @blur="handleEventBlur(instanceId)"
                                placeholder="输入数量"
                            />
                        </view>
                    </template>
                    <template #right-button="{ instanceId, disabled }">
                        <view 
                            class="event-button event-button-increase" 
                            :class="{ 'event-button-disabled': disabled }"
                            @click="handleEventIncrease(instanceId)"
                        >
                            <text class="event-button-text">➕</text>
                        </view>
                    </template>
                </hbxw-stepper>
                <text class="demo-value">当前值: {{ eventValue }}</text>
            </view>

            <view class="demo-item">
                <text class="demo-label">外部事件控制:</text>
                <view class="external-controls">
                    <button 
                        class="external-btn external-btn-decrease" 
                        @click="externalEventDecrease"
                    >
                        外部减少
                    </button>
                    <button 
                        class="external-btn external-btn-increase" 
                        @click="externalEventIncrease"
                    >
                        外部增加
                    </button>
                    <button 
                        class="external-btn external-btn-random" 
                        @click="externalSetRandom"
                    >
                        随机数值
                    </button>
                </view>
                <text class="demo-value">说明:直接通过instanceId控制步进器</text>
            </view>
        </view>
    </view>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue';
import hbxwStepper from '@/uni_modules/hbxw-stepper/components/hbxw-stepper/hbxw-stepper.vue';

// 响应式数据
const value1 = ref(1);
const value2 = ref(5);
const value3 = ref(10);
const value4 = ref(1.0);
const value5 = ref(500);
const value6 = ref(1);
const cartItem1 = ref(0);
const cartItem2 = ref(0);
const changeLogs = ref([]);
const customValue1 = ref(5);   // 圆形按钮初始值
const customValue2 = ref(20);  // 图标按钮初始值  
const customValue3 = ref(25);  // 自定义输入框初始值
const eventValue = ref(10);    // 事件通知控制初始值

// 新增:禁用控制和自定义样式相关的响应式数据
const disableLeftValue = ref(5);
const disableRightValue = ref(5);
const disableInputValue = ref(5);
const dynamicDisableValue = ref(5);
const customDisableStyleValue = ref(5);
const customEnableStyleValue = ref(5);

// 动态禁用控制状态
const dynamicLeftDisabled = ref(false);
const dynamicRightDisabled = ref(false);
const dynamicInputDisabled = ref(false);
const customStyleDisabled = ref(false);

// 自定义禁用样式
const customDisabledLeftStyle = ref({
    backgroundColor: '#ff4757',
    color: '#fff',
    border: '1px solid #ff4757',
    opacity: '0.8'
});

const customDisabledRightStyle = ref({
    backgroundColor: '#ff4757',
    color: '#fff',
    border: '1px solid #ff4757',
    opacity: '0.8'
});

const customDisabledInputStyle = ref({
    backgroundColor: '#ffcccc',
    color: '#999',
    border: '1px solid #ff4757',
    opacity: '0.7'
});

// 自定义启用样式
const customEnabledLeftStyle = ref({
    backgroundColor: '#5dade2',
    color: '#fff',
    border: '1px solid #5dade2',
    borderRadius: '50%'
});

const customEnabledRightStyle = ref({
    backgroundColor: '#5dade2',
    color: '#fff',
    border: '1px solid #5dade2',
    borderRadius: '50%'
});

const customEnabledInputStyle = ref({
    backgroundColor: '#e8f4f8',
    color: '#2c3e50',
    border: '2px solid #5dade2',
    borderRadius: '8px',
    fontWeight: 'bold'
});

// 计算属性
const totalItems = computed(() => {
    return cartItem1.value + cartItem2.value;
});

// 生命周期
onMounted(() => {
    console.log('页面已挂载,事件通知步进器instanceId: event-stepper-demo');
});

// 事件处理函数
const handleChange1 = (val) => {
    console.log('基础步进器值变化:', val);
};

const handleChange2 = (val) => {
    console.log('范围步进器值变化:', val);
};

const handleChange3 = (val) => {
    console.log('步长步进器值变化:', val);
};

const handleChange4 = (val) => {
    console.log('小数步进器值变化:', val);
};

const handleChange5 = (val) => {
    console.log('大数值步进器值变化:', val);
};

const handleChange6 = (val) => {
    const now = new Date().toLocaleTimeString();
    changeLogs.value.unshift(`${now}: 值变化为 ${val}`);
    // 限制日志数量
    if (changeLogs.value.length > 10) {
        changeLogs.value.pop();
    }
};

const handleCartChange1 = (val) => {
    console.log('购物车商品A数量变化:', val);
};

const handleCartChange2 = (val) => {
    console.log('购物车商品B数量变化:', val);
};

// 新增:禁用控制和自定义样式相关的事件处理函数
const handleDisableLeftChange = (val) => {
    console.log('左按钮禁用步进器值变化:', val);
};

const handleDisableRightChange = (val) => {
    console.log('右按钮禁用步进器值变化:', val);
};

const handleDisableInputChange = (val) => {
    console.log('输入框禁用步进器值变化:', val);
};

const handleDynamicDisableChange = (val) => {
    console.log('动态禁用步进器值变化:', val);
};

const handleCustomDisableStyleChange = (val) => {
    console.log('自定义禁用样式步进器值变化:', val);
};

const handleCustomEnableStyleChange = (val) => {
    console.log('自定义启用样式步进器值变化:', val);
};

// 动态禁用控制方法
const toggleLeftDisabled = () => {
    dynamicLeftDisabled.value = !dynamicLeftDisabled.value;
    console.log('左按钮禁用状态:', dynamicLeftDisabled.value);
};

const toggleRightDisabled = () => {
    dynamicRightDisabled.value = !dynamicRightDisabled.value;
    console.log('右按钮禁用状态:', dynamicRightDisabled.value);
};

const toggleInputDisabled = () => {
    dynamicInputDisabled.value = !dynamicInputDisabled.value;
    console.log('输入框禁用状态:', dynamicInputDisabled.value);
};

const toggleCustomStyleDisabled = () => {
    customStyleDisabled.value = !customStyleDisabled.value;
    console.log('自定义样式禁用状态:', customStyleDisabled.value);
};

const handleCustomChange1 = (val) => {
    console.log('自定义圆形按钮数量变化:', val);
};

const handleCustomChange2 = (val) => {
    console.log('自定义图标按钮数量变化:', val);
};

const handleCustomChange3 = (val) => {
    console.log('自定义输入框数量变化:', val);
};

// 自定义圆形按钮的方法
const customStepper1 = ref(null);
const handleCustomDecrease1 = () => {
    console.log('圆形按钮减少');
    if (customStepper1.value) {
        customStepper1.value.decreaseQuantity();
    }
};

const handleCustomIncrease1 = () => {
    console.log('圆形按钮增加');
    if (customStepper1.value) {
        customStepper1.value.increaseQuantity();
    }
};

// 自定义图标按钮的方法
const customStepper2 = ref(null);
const handleCustomDecrease2 = () => {
    console.log('图标按钮减少');
    if (customStepper2.value) {
        customStepper2.value.decreaseQuantity();
    }
};

const handleCustomIncrease2 = () => {
    console.log('图标按钮增加');
    if (customStepper2.value) {
        customStepper2.value.increaseQuantity();
    }
};

// 自定义输入框的方法
const customStepper3 = ref(null);
const handleCustomInput3 = (e) => {
    console.log('自定义输入框输入');
    if (customStepper3.value) {
        customStepper3.value.handleInput(e);
    }
};

const handleCustomBlur3 = () => {
    console.log('自定义输入框失焦');
    if (customStepper3.value) {
        customStepper3.value.handleBlur();
    }
};

// 事件通知控制的方法
const eventStepper = ref(null);

const handleEventChange = (val) => {
    console.log('事件通知步进器值变化:', val);
};

const handleEventDecrease = (instanceId) => {
    console.log('事件通知减少, instanceId:', instanceId);
    // 使用uni-app全局事件系统
    uni.$emit('decrease' + instanceId);
};

const handleEventIncrease = (instanceId) => {
    console.log('事件通知增加, instanceId:', instanceId);
    // 使用uni-app全局事件系统
    uni.$emit('increase' + instanceId);
};

const handleEventInput = (instanceId, e) => {
    console.log('事件通知输入, instanceId:', instanceId, 'event:', e);
    // 使用uni-app全局事件系统
    uni.$emit('input' + instanceId, e);
};

const handleEventBlur = (instanceId) => {
    console.log('事件通知失焦, instanceId:', instanceId);
    // 使用uni-app全局事件系统
    uni.$emit('input-blur' + instanceId);
};

// 测试方法
const testEventDecrease = () => {
    console.log('测试减少事件');
    if (eventStepper.value && eventStepper.value.instanceId) {
        const eventName = 'decrease' + eventStepper.value.instanceId;
        console.log('发送测试减少事件:', eventName);
        uni.$emit(eventName);
        testResult.value = `已发送减少事件: ${eventName}`;
    } else {
        testResult.value = '错误: eventStepper或instanceId不存在';
        console.log('错误: eventStepper或instanceId不存在');
    }
};

const testEventIncrease = () => {
    console.log('测试增加事件');
    if (eventStepper.value && eventStepper.value.instanceId) {
        const eventName = 'increase' + eventStepper.value.instanceId;
        console.log('发送测试增加事件:', eventName);
        uni.$emit(eventName);
        testResult.value = `已发送增加事件: ${eventName}`;
    } else {
        testResult.value = '错误: eventStepper或instanceId不存在';
        console.log('错误: eventStepper或instanceId不存在');
    }
};

const showInstanceInfo = () => {
    if (eventStepper.value) {
        const info = `instanceId: ${eventStepper.value.instanceId || '未生成'}`;
        console.log('步进器信息:', info);
        testResult.value = info;
    } else {
        testResult.value = '错误: eventStepper未初始化';
    }
};

// 这些方法已经被新的外部事件控制方法替代
// 保留原有的外部控制逻辑作为备用

const externalSetRandom = () => {
    console.log('外部按钮设置随机值');
    const randomValue = Math.floor(Math.random() * 99) + 1;
    eventValue.value = randomValue;
};

// 外部事件控制方法(直接使用已知的instanceId)
const externalEventDecrease = () => {
    console.log('外部事件减少');
    const instanceId = 'event-stepper-demo';
    uni.$emit('decrease' + instanceId);
};

const externalEventIncrease = () => {
    console.log('外部事件增加');
    const instanceId = 'event-stepper-demo';
    uni.$emit('increase' + instanceId);
};
</script>

<style lang="scss" scoped>
.demo-container {
    padding: 40rpx;
    background-color: #f5f5f5;
    min-height: 100vh;
}

.demo-header {
    text-align: center;
    margin-bottom: 60rpx;
}

.demo-title {
    font-size: 48rpx;
    font-weight: bold;
    color: #333;
    display: block;
    margin-bottom: 20rpx;
}

.demo-subtitle {
    font-size: 32rpx;
    color: #666;
    display: block;
}

.demo-section {
    background-color: #fff;
    border-radius: 16rpx;
    padding: 40rpx;
    margin-bottom: 40rpx;
    box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
}

.section-title {
    font-size: 36rpx;
    font-weight: bold;
    color: #333;
    display: block;
    margin-bottom: 30rpx;
}

.demo-item {
    display: flex;
    align-items: center;
    margin-bottom: 30rpx;
    flex-wrap: wrap;
}

.demo-label {
    font-size: 28rpx;
    color: #666;
    margin-right: 20rpx;
    min-width: 200rpx;
}

.demo-value {
    font-size: 28rpx;
    color: #007aff;
    margin-left: 20rpx;
    font-weight: bold;
}

.change-log {
    margin-top: 30rpx;
    padding: 20rpx;
    background-color: #f8f8f8;
    border-radius: 8rpx;
}

.log-title {
    font-size: 28rpx;
    font-weight: bold;
    color: #333;
    display: block;
    margin-bottom: 20rpx;
}

.log-item {
    margin-bottom: 10rpx;
}

.log-text {
    font-size: 24rpx;
    color: #666;
    line-height: 1.5;
}

.cart-total {
    margin-top: 30rpx;
    padding: 20rpx;
    background-color: #f0f9ff;
    border-radius: 8rpx;
    border: 1px solid #e0f2fe;
}

.total-text {
    font-size: 32rpx;
    font-weight: bold;
    color: #0369a1;
    text-align: center;
    display: block;
}

/* 控制按钮样式 */
.control-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 20rpx;
    margin-top: 30rpx;
    justify-content: center;
}

.control-btn {
    padding: 20rpx 32rpx;
    font-size: 24rpx;
    border: 2rpx solid #ddd;
    border-radius: 8rpx;
    background-color: #fff;
    color: #666;
    transition: all 0.3s ease;
    min-width: 180rpx;
    text-align: center;

    &.active {
        background-color: #ff4757;
        color: #fff;
        border-color: #ff4757;
    }

    &.style-btn.active {
        background-color: #5dade2;
        border-color: #5dade2;
    }

    &:hover {
        background-color: #f8f9fa;
    }

    &.active:hover {
        opacity: 0.9;
    }
}

/* 自定义按钮样式 */
.custom-button {
    width: 60rpx;
    height: 60rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #fff;
    border: 2px solid #007aff;
    box-sizing: border-box;
    transition: all 0.3s;

    &.custom-button-circle {
        border-radius: 50%;
    }

    &.custom-icon-button {
        border-radius: 8rpx;
        background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
        border: none;
    }

    &.custom-button-disabled {
        opacity: 0.4;
        background-color: #f5f5f5;
        border-color: #ddd;
    }
}

/* 事件通知控制按钮样式 */
.event-button {
    width: 70rpx;
    height: 60rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    border: none;
    border-radius: 12rpx;
    box-sizing: border-box;
    transition: all 0.3s;
    box-shadow: 0 2rpx 8rpx rgba(102, 126, 234, 0.3);

    &.event-button-decrease {
        background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
        box-shadow: 0 2rpx 8rpx rgba(255, 107, 107, 0.3);
    }

    &.event-button-increase {
        background: linear-gradient(135deg, #51cf66 0%, #40c057 100%);
        box-shadow: 0 2rpx 8rpx rgba(81, 207, 102, 0.3);
    }

    &.event-button-disabled {
        opacity: 0.4;
        background: #f5f5f5;
        border: 1px solid #ddd;
        box-shadow: none;
    }

    &:active {
        transform: scale(0.95);
    }
}

.custom-button-text {
    font-size: 32rpx;
    font-weight: bold;
    color: #007aff;
}

.custom-icon {
    font-size: 24rpx;
    color: #fff;
}

.event-button-text {
    font-size: 28rpx;
    font-weight: bold;
    color: #fff;
}

.custom-input {
    width: 120rpx;
    height: 60rpx;
    text-align: center;
    font-size: 28rpx;
    color: #000;
    background-color: #f8f9fa;
    border: 2px solid #007aff;
    border-radius: 8rpx;
    outline: none;
    box-sizing: border-box;

    &:focus {
        border-color: #0056b3;
        background-color: #fff;
    }

    &::placeholder {
        color: #999;
        font-size: 24rpx;
    }
}

.event-input-container {
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
    border: 2px solid #6c757d;
    border-radius: 8rpx;
    box-shadow: inset 0 1rpx 3rpx rgba(0, 0, 0, 0.1);
    transition: all 0.3s;

    &:focus-within {
        border-color: #007aff;
        box-shadow: 0 0 0 3rpx rgba(0, 122, 255, 0.1);
    }
}

.event-input {
    width: 120rpx;
    height: 56rpx;
    text-align: center;
    font-size: 28rpx;
    color: #000;
    background: transparent;
    border: none;
    outline: none;
    box-sizing: border-box;

    &::placeholder {
        color: #6c757d;
        font-size: 24rpx;
    }
}

.external-controls {
    margin-top: 30rpx;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 20rpx;
}

.external-btn {
    width: 160rpx;
    height: 60rpx;
    background: linear-gradient(135deg, #007aff 0%, #0056b3 100%);
    color: #fff;
    border: none;
    border-radius: 12rpx;
    font-size: 28rpx;
    font-weight: bold;
    transition: all 0.3s;
    cursor: pointer;
    box-shadow: 0 2rpx 8rpx rgba(0, 122, 255, 0.3);

    &:active {
        transform: scale(0.95);
    }

    &.external-btn-decrease {
        background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
        box-shadow: 0 2rpx 8rpx rgba(255, 107, 107, 0.3);
    }

    &.external-btn-increase {
        background: linear-gradient(135deg, #51cf66 0%, #40c057 100%);
        box-shadow: 0 2rpx 8rpx rgba(81, 207, 102, 0.3);
    }

    &.external-btn-random {
        background: linear-gradient(135deg, #f39c12 0%, #e67e22 100%);
        box-shadow: 0 2rpx 8rpx rgba(243, 156, 18, 0.3);
    }

    &:disabled {
        opacity: 0.4;
        background: #f5f5f5;
        color: #999;
        box-shadow: none;
        cursor: not-allowed;
        transform: none;
    }
}
</style> 

API

Props

参数 类型 默认值 说明
modelValue Number 1 当前值,支持 v-model
value Number 1 Vue2 兼容,当前值
min Number 1 最小值
max Number 999 最大值
step Number 1 步长,支持小数
instanceId String '' 实例 ID,用于事件通知
leftDisabled Boolean false 左按钮禁用控制
rightDisabled Boolean false 右按钮禁用控制
inputDisabled Boolean false 输入框禁用控制
disabledLeftStyle Object/String {} 左按钮禁用样式
disabledRightStyle Object/String {} 右按钮禁用样式
disabledInputStyle Object/String {} 输入框禁用样式
enabledLeftStyle Object/String {} 左按钮启用样式
enabledRightStyle Object/String {} 右按钮启用样式
enabledInputStyle Object/String {} 输入框启用样式

Events

事件名 说明 回调参数
update:modelValue 值变化时触发 (value: number)
input Vue2 兼容,值变化时触发 (value: number)
change 值变化时触发 (value: number)

方法

方法名 说明 参数
decreaseQuantity 减少数量 -
increaseQuantity 增加数量 -
updateQuantity 更新数量 (value: number)
handleInput 处理输入 (event: Event)
handleBlur 处理失焦 -

插槽 (Slots)

插槽名 说明 作用域插槽参数
left-button 左侧减少按钮 { instanceId, disabled, quantity, min, max }
right-button 右侧增加按钮 { instanceId, disabled, quantity, min, max }
input 中间输入框 { value, quantity, min, max, instanceId, handleInput, handleBlur, disabled }

插槽参数说明

left-button / right-button 插槽参数:

  • instanceId: 组件实例 ID
  • disabled: 是否禁用按钮(考虑了手动禁用和边界限制)
  • quantity: 当前数量值
  • min / max: 最小值/最大值

input 插槽参数:

  • value: 当前输入框的值(字符串)
  • quantity: 当前数量值(数字)
  • min / max: 最小值/最大值
  • instanceId: 组件实例 ID
  • disabled: 是否禁用输入框

许可证

MIT License

注:包体积显示偏大,是因为有示例动图,真正打包到项目中的是很小的,可放心使用

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问