vue组件封装-input

效果展示

当然,以上用法均可以混合使用

使用

attributes

参数名描述类型可选值默认值
v-model绑定valuestring / number
clear-able展示清空图标booleanfalse
showPassword展示密码切换图标booleanfalse
showWordLimit展示字数限制booleanfalse
suffixIcon尾部图标string
prefixIcon头部图标string
showExt展示提示信息booleanfalse
extIcon提示信息的图标string叉图标
extInfo提示信息文本string
readonly只读booleanfalse
maxlength文本最大长度string / number
minlength文本最小长度string / number
placeholder占位文本string
disabled禁用booleanfalse
autofocus自动获取焦点booleanfalse
type输入框类型stringtext / passwordtext
id输入框idstring

设置其他的input框原生属性也会有效,但是编译器不会给出提示,由于目前用不到那么多原生属性所以并没有设置。

slots

槽名描述
prefixinput头部内容,强制限制了高度和input框高相等、边界为none.
suffixinput尾部内容,强制限制了高度和input框高相等、边界为none.

events

事件名描述回调参数
input输入内容时触发value: string | number
blurinput失去焦点时触发event:Event
focusinput获取焦点时触发event:Event
change在input内容发生了变化 且 (input失去焦点或者按下enter之后)event:Event
click在input被点击时触发event:Event
clear点击了清空图标后触发

methods

方法名描述参数
focus使得input获取焦点
blur使得input失去焦点
select选择input的文本

正文

实现思路

想要实现图标包裹在input内部的效果可以考虑:

  • 将input作为组件最外层元素,图标相对定位,但是由于input为单标签元素,不能作为图标的父元素提供定位基准
  • 将组件最外层元素设置为一个div,input、图标等作为div内部元素

于是组件结构为:

<div class="sss-input-outer">
    <!--            头部slot-->
    <div class="sss-input-prefix-slot"><slot name="prefix"></slot></div>

    <!--            头部icon-->
    <div class="sss-input-prefix-icon-box"></div>
    
    <!--            input-->
    <input class="sss-input-inner">
    
    <!--            尾部icon-->
    <div class="sss-input-suffix-icon-box"></div>

    <!--            尾部slot-->
    <div class="sss-input-suffix-slot"><slot name="suffix"></slot></div>
    
    <!--            文字提示-->
    <div class="sss-input-ext"></div>
</div>

这个组件不会涉及太多东西,就是为了美观css会多一点

html

<template>
    <div class="sss-input-outer" :class="computedClass"
         @click="$emit('click',$event)">
        <div class="sss-input-prefix">
            <slot name="prefix"></slot>
        </div>
        <span v-if="prefixIcon" class="iconfont sss-input-left-icon" :class="prefixIcon"></span>

        <input class="sss-input-inner"
               ref="inputInner"
               v-bind="$attrs"
               @input="$emit('input',$event.target.value)"
               @focus="$emit('focus', $event)"
               @blur="$emit('blur', $event)"
               @change="$emit('change', $event)"


               :readonly="readonly"
               :maxlength="maxlength"
               :minlength="minlength"
               :placeholder="placeholder"
               :disabled="disabled"
               :step="step"
               :autofocus="autofocus"
               :type="type"
               :id="id"
               :value="value"
        >

        <div v-if="clearAble||showPassword||suffixIcon||showWordLimit" class="sss-input-right-icon-box">
            <!--            清空-->
            <span v-if="clearAble && this.value"
                  class="iconfont icon-yuanxingdacha sss-input-icon-clear"
                  @click="__clearContext"></span>
            <!--            字数限制-->
            <i v-if="this.value.length > this.maxlength" class="iconfont icon-icon--jinggao"
               style="color: red;font-size: 12px"></i>

            <span v-if="showWordLimit && maxlength && (this.type==='text'||this.type==='textarea')"
                  style="font-size: 10px;color: gray">
                    {{ this.value?.length || 0 }}/{{ this.maxlength }}
            </span>
            <!--            密码显示-->
            <span v-if="showPassword && this.value && this.type==='password' "
                  class="iconfont icon-yanjing4 sss-input-icon-password"
                  @click="__showContext"></span>
            <!--            尾部icon-->
            <span v-if="suffixIcon" ref="suffixIcon" class="iconfont" :class="suffixIcon"></span>


        </div>
        <div class="sss-input-suffix">
            <slot name="suffix"></slot>
        </div>

        <transition name="sssInputFade">
            <label v-if="showExt" class="sss-input-ext" :for="id" ref="sss-input-ext">
                <span class="iconfont" :class="extIcon"></span>
                {{ this.extInfo }}
            </label>
        </transition>

    </div>
</template>

js

<script>
export default {
    name: "sss-input",
    props: {
        value: {
            type: String || Number,
            default: ""

        },
        clearAble: {
            type: Boolean,
            default: false
        },
        showPassword: {
            type: Boolean,
            default: false,
        },
        showWordLimit: {
            type: Boolean,
            default: false,
        },
        suffixIcon: {
            type: String,
            default: undefined
        },
        prefixIcon: {
            type: String,
            default: undefined
        },

        showExt: {type: Boolean, default: false},
        extIcon: {default: "icon-yuanxingdacha"},
        extInfo: {default: "不符合预期"},


        readonly: {
            type: Boolean,
            default: false
        },


        maxlength: {},
        minlength: {},
        placeholder: {},
        disabled: {
            type: Boolean,
            default: false
        },
        autofocus: {
            type: Boolean,
            default: false
        },
        step: {},
        type: {default: 'text'},
        id: {}
    },
    computed: {
        computedClass() {
            return {
                disabled: this.disabled
            }
        }
    },
    methods: {
        __clearContext() {
            this.$emit('input', "");
            this.$emit('clear');
            this.$refs.inputInner.focus();
        },
        __showContext() {
            if (this.$refs.inputInner.type === 'password') {
                this.$refs.inputInner.type = 'text';
            } else {
                this.$refs.inputInner.type = 'password';
            }
            this.$refs.inputInner.focus();
        },
        __reverseSuffixIcon() {
            this.$refs.suffixIcon.classList.toggle("reverse");
        },
        __correctSuffixIcon() {
            if (this.$refs.suffixIcon.classList.contains('reverse')) {
                this.$refs.suffixIcon.classList.remove('reverse');
            }
        },

        focus() {
            this.$refs.inputInner.focus();
        },
        blur() {
            this.$refs.inputInner.blur();
        },
        select() {
            this.$refs.inputInner.select();
        },


    },


}
</script>

css

<style lang="less">
@import "@/assets/style/sss-var.less";

.reverse {
    transform: rotate(-180deg);
}

.sss-input-outer {
    box-sizing: border-box;
    width: 100%;
    height: 40px;
    border: solid 1px @color-gray;
    border-radius: 5px;
    display: inline-flex;
    flex-flow: row nowrap;
    justify-content: center;
    align-items: center;
    user-select: none;
    position: relative;
    font-size: @font-size-s;
    transition: all .3s;

    & * { //继承
        color: @color-black1;
        font-size: inherit;
        box-sizing: border-box;
    }

    //去除默认样式
    & input {
        border: none;
        padding: 10px;
        width: 100%;
        display: inline-block;
        border-radius: inherit;


        &:focus {
            outline: none;
        }

        &::placeholder {
            color: @color-gray;
        }

        &[type="password"]::-ms-reveal {
            display: none !important;

        }
    }


    &:hover { //覆盖时 边界变化 且 清空图标出现
        border: solid 1px darken(@color-gray, 10%);

        .sss-input-icon-clear {
            display: inline;
        }
    }

    &:focus-within { //内部元素聚焦时,边界变化 图标展示
        border: solid 1px @color-main;
        box-shadow: 0 0 5px 0 darken(@color-main, -30%);


        .sss-input-icon-password, .sss-input-icon-clear {
            display: inline;
        }

    }


    &.disabled {
        background: @color-white3;

        & * {
            color: @color-gray;
        }

        .sss-input-right-icon-box {
            display: none !important;
        }

    }

}

//左图标
.sss-input-left-icon {
    padding-left: 10px;
    color: @color-black1;
}

//右图标盒子
.sss-input-right-icon-box {
    display: flex;
    justify-content: center;
    align-items: center;
    color: @color-gray;

    & * { //内部元素默认边距
        padding: 0 6px;
        color: inherit;
        transition: all .3s;
    }

    & .sss-input-icon-password, & .sss-input-icon-clear {
        display: none;
        cursor: pointer;

        &:hover {
            + span {
                display: inline;
            }

            display: inline;

            &:active {
                + span {
                    display: inline;
                }
            }
        }
    }

    & .sss-input-icon-password {
        display: inline;
    }


}

.sss-input-suffix {
    flex: none;
    display: flex;
    justify-content: center;
    align-items: center;
    border-left: solid 1px @color-gray;
    height: 100%;
    background: #f1f2f6;
    border-radius: 0 5px 5px 0;
    overflow: hidden;

    & > * {
        height: 100% !important;
        border: none !important;
        border-radius: 0 !important;
    }

    &:empty {
        display: none;
    }
}

.sss-input-prefix {
    flex: none;
    display: flex;
    justify-content: center;
    align-items: center;
    border-right: solid 1px @color-gray;
    height: 100%;
    background: #f1f2f6;
    border-radius: 5px 0 0 5px;
    overflow: hidden;

    & > * {
        height: 100% !important;
        border: none !important;
        border-radius: 0 !important;
    }

    &:empty {
        display: none;
    }
}

.sss-input-ext {
    position: absolute;
    bottom: -22px;
    left: 1px;
    color: red;
    font-size: 10px;

    & * {

        color: red !important;
        padding-right: 5px;

        &:before {
            font-size: 15px;
        }
    }

    display: flex;
    justify-content: center;
    align-items: center;

}

.sssInputFade-enter-active {
    animation: sssFadeDownIn .3s;
}

.sssInputFade-leave-active {
    animation: sssFadeIn .3s reverse;
}


</style>
    position: absolute;
    bottom: -22px;
    left: 1px;
    color: red;
    font-size: 10px;

    & * {

        color: red !important;
        padding-right: 5px;

        &:before {
            font-size: 15px;
        }
    }

    display: flex;
    justify-content: center;
    align-items: center;

}

.sssInputFade-enter-active {
    animation: sssFadeDownIn .3s;
}

.sssInputFade-leave-active {
    animation: sssFadeIn .3s reverse;
}


</style>
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值