测试环境链接替换,部分问题修复,增加批量退菜功能

This commit is contained in:
2026-07-07 15:26:08 +08:00
parent b93429ed99
commit c001f982b4
29 changed files with 1975 additions and 349 deletions

View File

@@ -0,0 +1,201 @@
<template>
<div class="custom-number-box">
<div
class="number-btn number-btn-minus"
:class="{ disabled: isMinDisabled || isDisabled }"
@click="handleMinus"
>
<span>-</span>
</div>
<input
v-model="inputValue"
class="number-input"
type="text"
:disabled="isDisabled"
@input="handleInput"
@blur="handleBlur"
/>
<div
class="number-btn number-btn-plus"
:class="{ disabled: isMaxDisabled || isDisabled }"
@click="handlePlus"
>
<span>+</span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch, computed } from 'vue'
const props = defineProps({
modelValue: {
type: [Number, String],
default: ''
},
min: {
type: Number,
default: 1
},
max: {
type: Number,
default: 28
},
step: {
type: Number,
default: 1
},
disabled: {
type: Boolean,
default: false
},
// ✅ 新增:是否整数(默认 true
integer: {
type: Boolean,
default: true
}
})
const emit = defineEmits(['update:modelValue'])
const inputValue = ref('')
const isDisabled = computed(() => props.disabled)
// 监听外部值
watch(
() => props.modelValue,
(val) => {
if (val === '' || val === null || val === undefined) {
inputValue.value = ''
return
}
const num = Number(val)
inputValue.value = isNaN(num) ? '' : String(num)
},
{ immediate: true }
)
// 减号禁用
const isMinDisabled = computed(() => {
if (inputValue.value === '') return true
const val = Number(inputValue.value)
return val <= props.min
})
// 加号禁用
const isMaxDisabled = computed(() => {
if (inputValue.value === '') return false
const val = Number(inputValue.value)
return val >= props.max
})
// 减
function handleMinus() {
if (inputValue.value === '') return
let val = Number(inputValue.value) - props.step
val = Math.max(val, props.min)
if (props.integer) val = Math.round(val)
inputValue.value = String(val)
emit('update:modelValue', val)
}
// 加
function handlePlus() {
let val
if (inputValue.value === '') {
val = props.min
} else {
val = Number(inputValue.value) + props.step
}
val = Math.min(val, props.max)
if (props.integer) val = Math.round(val)
inputValue.value = String(val)
emit('update:modelValue', val)
}
// 输入时处理
function handleInput() {
let val = inputValue.value.trim()
if (val === '') {
emit('update:modelValue', '')
return
}
// 如果是整数,禁止输入小数点
if (props.integer) val = val.replace(/\./g, '')
const num = Number(val)
if (isNaN(num)) {
inputValue.value = ''
emit('update:modelValue', '')
return
}
inputValue.value = val
emit('update:modelValue', num)
}
// 失焦校验
function handleBlur() {
const val = inputValue.value.trim()
if (val === '') {
emit('update:modelValue', '')
return
}
let num = Number(val)
if (isNaN(num)) {
inputValue.value = ''
emit('update:modelValue', '')
return
}
// 限制范围
num = Math.max(props.min, Math.min(props.max, num))
// ✅ 整数控制
if (props.integer) num = Math.round(num)
inputValue.value = String(num)
emit('update:modelValue', num)
}
</script>
<style scoped>
.custom-number-box {
display: inline-flex;
align-items: center;
border: 1px solid #e4e7ed;
border-radius: 4px;
overflow: hidden;
user-select: none;
}
.number-btn {
width: 36px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: #f5f7fa;
cursor: pointer;
font-size: 24px;
}
.number-btn.disabled {
color: #c0c4cc;
cursor: not-allowed;
}
.number-input {
width: 50px;
height: 32px;
text-align: center;
border: none;
outline: none;
font-size: 14px;
padding: 0;
}
</style>