货无忧
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

271 lines
5.4 KiB

<template>
<view :class="inputClassName" ref="inputRef">
<input class="input" v-model="inputValue" @blur="handleBlur" @input="handleInput" :placeholder="props.placeholder"
:type="props.type" @confirm="handleConfirm" />
<view class="messageBox" v-if="props.rules.required">
{{props.rules.message}}
</view>
<!-- 移除按钮 -->
<view class="statusIconBox">
<view
:class="{removeIcon:true, showClearable: inputClassName.success, removeClearable: !inputClassName.success}"
v-if="clearable">
<u-icon :name="statusIcon" :color="inputClassName.success ?'#3AD8BC' : '#F8544B'" size="40"></u-icon>
</view>
</view>
<view class="removeIconBox">
<view @click="()=>{
inputValue = ''
handleChangeInputStatus()
}" :class="{removeIcon:true, showClearable: inputValue, removeClearable: !inputValue}" v-if="clearable">
<u-icon name="close-circle-fill" color="#8f8f8f" size="40"></u-icon>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { computed, defineProps, defineEmits, ref, nextTick, type PropType } from 'vue';
type rulesType = {
/** 警示信息 */
message : string,
/** 是否进行值的检测 */
required : boolean,
/** 检测规则 */
rulesFn ?: Function,
trigger : string | string[]
}
const props = defineProps({
/** 双向绑定的值 */
modelValue: {
type: Number as PropType<Number> || String as PropType<String>,
required: true
},
/** 输入框类型 */
type: {
type: String as PropType<String>,
required: false,
default: 'text'
},
/** 验证规则 */
rules: {
type: Object as PropType<rulesType>,
required: false,
default: {
message: '',
required: true
} as any
},
/** 是否显示清除按钮 */
clearable: {
type: Boolean as PropType<Boolean>,
required: false,
default: true
},
/** 提示 */
placeholder: {
type: String as PropType<String>,
required: false,
default: ''
},
/** 是否聚焦 */
focus: {
type: Boolean as PropType<Boolean>,
required: false,
default: false
}
})
/** 输入框实例 */
const inputRef = ref()
/** 状态Icon */
const statusIcon = ref('')
/** 输入框类名 */
const inputClassName = ref({
input_container: true
})
const emit = defineEmits(['input', 'change', 'blur', 'change', 'update:modelValue', 'confirm'])
const inputValue = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
/** 输入框状态改变 */
const handleChangeInputStatus = async () => {
if (!props.rules.required) return
await nextTick()
statusIcon.value = ''
delete inputClassName.value.success
delete inputClassName.value.error
let isSuccess = false
if (props.rules.rulesFn) isSuccess = props.rules.rulesFn(inputValue.value)
else isSuccess = Boolean(inputValue.value || inputValue.value === 0)
isSuccess ? inputClassName.value.success = true : inputClassName.value.error = true
if (isSuccess) {
inputClassName.value.success = true
statusIcon.value = 'checkmark-circle-fill'
} else {
inputClassName.value.error = true
statusIcon.value = 'info-circle-fill'
}
}
/** 输入框值变化时 */
const handleChange = (newValue : string | number, oldValue : string | number) => {
emit('change', newValue, oldValue)
}
/** 输入框失焦事件 */
const handleBlur = (event) => {
emit('blur', event)
handleChangeInputStatus()
}
/** 输入框事件 */
const handleInput = (event) => {
emit('blur', event)
handleChangeInputStatus()
}
const handleConfirm = (event) => {
emit('confirm', event)
}
</script>
<style scoped lang="scss">
@keyframes showIcon {
from {
transform: translate(-50%, -200%);
opacity: 0;
}
to {
transform: translate(-50%, -50%);
opacity: 1;
}
}
@keyframes romveIcon {
from {
transform: translate(-50%, -50%);
opacity: 1;
}
to {
transform: translate(-50%, -200%);
opacity: 0;
}
}
/** 动画时间 */
$animationTime: 0.3s;
/** 验证成功颜色 */
$successColor: #3AD8BC;
/** 验证失败颜色 */
$errorColor: #F8544B;
.input_container {
display: flex;
position: relative;
overflow: hidden;
// height: 80upx;
height: 100%;
transition: all $animationTime;
border: 4upx solid transparent;
// background: #fff;
border-radius: 10upx;
&.success {
border: 4upx solid $successColor;
.removeIconBox {
width: 60upx;
}
}
&.error {
border: 4upx solid $errorColor;
.removeIconBox {
width: 0upx;
}
}
.removeIconBox {
position: relative;
height: 100%;
width: 0upx;
flex: none;
transition: all $animationTime;
overflow: hidden;
.removeIcon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(50%, -200%);
&.showClearable {
animation: showIcon $animationTime forwards;
}
&.removeClearable {
animation: romveIcon $animationTime forwards;
}
}
}
.statusIconBox {
position: relative;
height: 100%;
width: 60upx;
flex: none;
transition: all $animationTime;
overflow: hidden;
.removeIcon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
:deep(.u-icon__icon) {
color: $errorColor !important;
transition: all $animationTime;
}
&.showClearable {
:deep(.u-icon__icon) {
color: $successColor !important;
}
}
}
}
}
.input {
height: 100%;
flex: 1;
padding: 0 20upx;
}
</style>