基础用法
首先先新建一个input.vue
文件,然后写入一个最基本的input
输入框
<template> <div class="k-input"> <input class="k-input__inner" /> </div> </template>
然后在我们的 vue 项目examples
下的app.vue
引入Input
组件
<template> <div class="Shake-demo"> <Input /> </div> </template> <script lang="ts" setup> import { Input } from "kitty-ui"; </script>
此时页面上便出现了原生的输入框,所以需要对这个输入框进行样式的添加,在input.vue
同级新建style/index.less
,Input
样式便写在这里
.k-input { font-size: 14px; display: inline-block; position: relative; .k-input__inner { background-color: #fff; border-radius: 4px; border: 1px solid #dcdfe6; box-sizing: border-box; color: #606266; display: inline-block; font-size: inherit; height: 40px; line-height: 40px; outline: none; padding: 0 15px; width: 100%; &::placeholder { color: #c2c2ca; } &:hover { border: 1px solid #c0c4cc; } &:focus { border: 1px solid #409eff; } } }
接下来要实现Input
组件的核心功能:双向数据绑定。当我们在 vue 中使用input
输入框的时候,我们可以直接使用v-model
来实现双向数据绑定,v-model
其实就是value @input
结合的语法糖。而在 vue3 组件中使用v-model
则表示的是modelValue @update:modelValue
的语法糖。比如Input
组件为例
<Input v-model="tel" />
其实就是
<Input :modelValue="tel" @update:modelValue="tel = $event" />
所以在input.vue
中我们就可以根据这个来实现Input
组件的双向数据绑定,这里我们使用setup
语法
<template> <div class="k-input"> <input class="k-input__inner" :value="inputProps.modelValue" @input="changeInputVal" /> </div> </template> <script lang="ts" setup> //组件命名 defineOptions({ name: "k-input", }); //组件接收的值类型 type InputProps = { modelValue?: string | number; }; //组件发送事件类型 type InputEmits = { (e: "update:modelValue", value: string): void; }; //withDefaults可以为props添加默认值等 const inputProps = withDefaults(defineProps<InputProps>(), { modelValue: "", }); const inputEmits = defineEmits<InputEmits>(); const changeInputVal = (event: Event) => { inputEmits("update:modelValue", (event.target as HTMLInputElement).value); }; </script>
到这里基础用法
就完成了,接下来开始实现禁用状态
禁用状态
这个比较简单,只要根据props
的disabled
来赋予禁用类名即可
<template> <div class="k-input" :class="styleClass"> <input class="k-input__inner" :value="inputProps.modelValue" @input="changeInputVal" :disabled="inputProps.disabled" /> </div> </template> <script lang="ts" setup> //... type InputProps = { modelValue?: string | number; disabled?: boolean; }; //... //根据props更改类名 const styleClass = computed(() => { return { "is-disabled": inputProps.disabled, }; }); </script>
然后给is-disabled
写些样式
//... .k-input.is-disabled { .k-input__inner { background-color: #f5f7fa; border-color: #e4e7ed; color: #c0c4cc; cursor: not-allowed; &::placeholder { color: #c3c4cc; } } }
尺寸
按钮尺寸包括medium
,small
,mini
,不传则是默认尺寸。同样的根据props
的size
来赋予不同类名
const styleClass = computed(() => { return { "is-disabled": inputProps.disabled, [`k-input--${inputProps.size}`]: inputProps.size, }; });
然后写这三个类名的不同样式
//... .k-input.k-input--medium { .k-input__inner { height: 36px; &::placeholder { font-size: 15px; } } } .k-input.k-input--small { .k-input__inner { height: 32px; &::placeholder { font-size: 14px; } } } .k-input.k-input--mini { .k-input__inner { height: 28px; &::placeholder { font-size: 13px; } } }
继承原生 input 属性
原生的input
有type
,placeholder
等属性,这里可以使用 vue3 中的useAttrs
来实现props
穿透.子组件可以通过v-bind
将props
绑定
<template> <div class="k-input" :class="styleClass"> <input class="k-input__inner" :value="inputProps.modelValue" @input="changeInputVal" :disabled="inputProps.disabled" v-bind="attrs" /> </div> </template> <script lang="ts" setup> //... const attrs = useAttrs(); </script>
可清空
通过clearable
属性、Input
的值是否为空以及是否鼠标是否移入来判断是否需要显示可清空图标。图标则使用组件库的Icon
组件
<template> <div class="k-input" @mouseenter="isEnter = true" @mouseleave="isEnter = false" :class="styleClass" > <input class="k-input__inner" :disabled="inputProps.disabled" v-bind="attrs" :value="inputProps.modelValue" @input="changeInputVal" /> <div @click="clearValue" v-if="inputProps.clearable && isClearAbled" v-show="isFoucs" class="k-input__suffix" > <Icon name="error" /> </div> </div> </template> <script setup lang="ts"> //... import Icon from "../icon/index"; //... //双向数据绑定&接收属性 type InputProps = { modelValue?: string | number; disabled?: boolean; size?: string; clearable?: boolean; }; //... const isClearAbled = ref(false); const changeInputVal = (event: Event) => { //可清除clearable (event.target as HTMLInputElement).value ? (isClearAbled.value = true) : (isClearAbled.value = false); inputEmits("update:modelValue", (event.target as HTMLInputElement).value); }; //清除input value const isEnter = ref(true); const clearValue = () => { inputEmits("update:modelValue", ""); }; </script>
清除图标部分 css 样式
.k-input__suffix { position: absolute; right: 10px; height: 100%; top: 0; display: flex; align-items: center; cursor: pointer; color: #c0c4cc; }
密码框 show-password
通过传入show-password
属性可以得到一个可切换显示隐藏的密码框。这里要注意的是如果传了clearable
则不会显示切换显示隐藏的图标
<template> <div class="k-input" @mouseenter="isEnter = true" @mouseleave="isEnter = false" :class="styleClass" > <input ref="ipt" class="k-input__inner" :disabled="inputProps.disabled" v-bind="attrs" :value="inputProps.modelValue" @input="changeInputVal" /> <div class="k-input__suffix" v-show="isShowEye"> <Icon @click="changeType" :name="eyeIcon" /> </div> </div> </template> <script setup lang="ts"> //... const attrs = useAttrs(); //... //显示隐藏密码框 showPassword const ipt = ref(); Promise.resolve().then(() => { if (inputProps.showPassword) { ipt.value.type = "password"; } }); const eyeIcon = ref("browse"); const isShowEye = computed(() => { return ( inputProps.showPassword && inputProps.modelValue && !inputProps.clearable ); }); const changeType = () => { if (ipt.value.type === "password") { eyeIcon.value = "eye-close"; ipt.value.type = attrs.type || "text"; return; } ipt.value.type = "password"; eyeIcon.value = "browse"; }; </script>
这里是通过获取
input
元素,然后通过它的type
属性进行切换,其中browse
和eye-close
分别是Icon
组件中眼睛开与闭,效果如下