2024-03-27 21:02:55 +08:00

284 lines
7.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<!-- <div class="number-input">
<p v-if="label" class="input-label">
{{ label }}
</p>
<div :class="{ 'input-wrap': true, active: inputBorder }">
<input :class="{ 'real-input': true, disable: !editable }" type="text" :value="modelValue" :readonly="editable ? false : 'readonly'" @input="updateValue($event.target.value)" @focus="focusInput" @blur="blurInput" @keyup="verifyNumber" @keydown="(e) => opNumber(e)" />
<div v-if="editable" class="op-btn">
<div class="up" @click="up"><i class="iconfont icon-up" /></div>
<div class="down" @click="down"><i class="iconfont icon-down" /></div>
</div>
</div>
</div> -->
<div v-if="type === 'simple'">
<span class="prepend">{{ prepend }}</span>
<input
:class="{ 'small-input': true, disable: !editable }" type="text" :value="modelValue"
:readonly="!props.editable"
@input="updateValue($event && $event.target ? ($event.target as HTMLInputElement).value : '')"
@focus="focusInput"
@blur="blurInput"
@keyup="verifyNumber"
@keydown="(e) => opNumber(e)" />
</div>
<div v-else class="number-input2">
<div class="input-wrap">
<input
:class="{ 'real-input': true, disable: !editable }"
type="text" :value="modelValue" :readonly="!props.editable"
@input="updateValue($event && $event.target ? ($event.target as HTMLInputElement).value : '')"
@focus="focusInput"
@blur="blurInput"
@keyup="verifyNumber"
@keydown="(e) => opNumber(e)"
/>
</div>
<span style="color: rgba(0, 0, 0, 0.45)">{{ label }}</span>
<!-- <div :class="{ 'input-wrap': true, active: inputBorder }">
<input :class="{ 'real-input': true, disable: !editable }" type="text" :value="modelValue" :readonly="editable ? false : 'readonly'" @input="updateValue($event.target.value)" @focus="focusInput" @blur="blurInput" @keyup="verifyNumber" @keydown="(e) => opNumber(e)" />
<div v-if="editable" class="op-btn">
<div class="up" @click="up"><i class="iconfont icon-up" /></div>
<div class="down" @click="down"><i class="iconfont icon-down" /></div>
</div>
</div> -->
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, watch } from 'vue'
// 数字输入组件
// const NAME = 'number-input'
type TProps = {
label?: string
modelValue?: string | number
editable?: boolean
step?: number
maxValue?: string | number
minValue?: string | number
type?: string
prepend?: string
}
type TEmits = {
(event: 'finish', data: number | string): void
(event: 'update:modelValue', data: number | string): void
}
const props = withDefaults(defineProps<TProps>(), {
label: '',
modelValue: '',
editable: true,
step: 1
})
const emit = defineEmits<TEmits>()
const inputBorder = ref<boolean>(false)
const tagText = ref<string | number>('')
const showEdit = ref<boolean>(false)
watch(
() => props.modelValue,
() => fixedNum()
)
onMounted(() => {
fixedNum()
})
function fixedNum() {
// 小数点过长时
const decimal = String(props.modelValue).split('.')[1]
if (decimal && decimal.length > 2) {
setTimeout(() => {
updateValue(Number(props.modelValue).toFixed(2))
}, 10)
}
// 限定数字范围
if (props.maxValue && props.modelValue > props.maxValue) {
setTimeout(() => {
updateValue(Number(props.maxValue))
}, 10)
} else if (typeof props.minValue === 'number' && Number(props.modelValue) < Number(props.minValue)) {
setTimeout(() => {
updateValue(Number(props.minValue))
}, 10)
}
}
function updateValue(value: string | number) {
emit('update:modelValue', value === '-' ? '-' : Number(value))
}
function up() {
updateValue(parseInt(`${props.modelValue}` ?? '0', 10) + props.step)
}
function down() {
let value = parseInt(`${props.modelValue}` ?? '0', 10) - props.step
updateValue(value)
}
function opNumber(e: KeyboardEvent) {
e.stopPropagation()
switch (e.keyCode) {
case 38:
up()
return
case 40:
down()
return
}
}
function verifyNumber() {
let value = String(props.modelValue)
let len = value.length
let newValue = ''
let isNegative = value[0] === '-'
// 判断是否连续字符否则置为0
for (let i = isNegative ? 1 : 0; i < len; ++i) {
let c = value[i]
if (c == '.' || (c >= '0' && c <= '9')) {
newValue += c
} else {
break
}
}
if (newValue === '') {
newValue = '0'
}
if (isNegative) {
newValue = '-' + (newValue === '0' ? '' : newValue)
}
updateValue(newValue)
// this.updateValue(parseInt(newValue, 10))
}
function focusInput() {
inputBorder.value = true
tagText.value = props.modelValue
}
function blurInput() {
if (props.modelValue === '-') {
updateValue(0)
}
inputBorder.value = false
if (props.modelValue !== tagText.value) {
emit('finish', props.modelValue)
}
}
</script>
<style lang="less" scoped>
@color0: #e1e1e1;
@color1: #d1d1d1;
.number-input {
height: 60px;
line-height: 1.15;
width: 60px;
.input-label {
user-select: none;
font-size: 12px;
line-height: 12px;
padding: 8px 0;
}
.input-wrap {
border-radius: 3px;
border: 1px solid @color0;
display: flex;
flex-direction: row;
width: 60px;
.real-input {
border-radius: 3px;
border: 0px;
font-size: 12px;
height: 30px;
outline: none;
padding: 5px;
text-align: center;
width: 100%;
}
.real-input.disable {
color: #666666;
cursor: not-allowed;
}
.op-btn {
border-left: 1px solid @color0;
display: flex;
flex-direction: column;
height: 30px;
width: 14px;
.up,
.down {
width: 13px;
transform: scale(0.8);
.iconfont {
font-size: 12px;
color: rgba(0, 0, 0, 0.5);
}
.iconfont:hover {
font-size: 14px;
color: rgba(0, 0, 0, 0.9);
}
}
// .down {
// }
}
}
.input-wrap.active {
border: 1px solid rgba(59, 116, 241, 0.7);
box-shadow: 1px 1px 5px 2px rgba(59, 116, 241, 0.1);
}
}
.number-input2 {
margin-right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
width: 45%;
height: 40px;
background: #f3f5f7;
border-radius: 4px;
font-size: 14px;
color: #33383e;
padding: 0 16px 0 11px;
cursor: pointer;
.input-wrap {
display: flex;
}
.real-input {
background: transparent;
border-radius: 3px;
border: 0px;
font-size: 12px;
height: 30px;
outline: none;
padding: 5px;
text-align: left;
width: 100%;
}
}
.small-input {
font-size: 12px;
width: 100%;
padding: 4px 7px;
box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
cursor: text;
}
.prepend {
font-size: 12px;
position: absolute;
margin: -14px 0 0 3px;
transform: scale(0.85);
transform-origin: left;
color: #888888;
}
</style>