Vue3 input box (Input)

APIs

parameter illustrate type Defaults must pass
width input box width string | number ‘100%’ false
addonBefore Set pre-label string | slot ‘’ false
addonAfter set post tag string | slot ‘’ false
allowClear Content can be deleted by clicking the clear icon boolean false false
password Whether to enable the password box boolean false false
disabled Whether to disable boolean false false
maxlength The maximum length number undefined false
showCount Whether to display the word count boolean false false
size input box size ‘large’ | ‘middle’ | ‘small’ ‘middle’ false
prefix prefix icon string ‘’ false
suffix suffix icon string ‘’ false
value(v-model) input box content string ‘’ false

Events

event name illustrate parameter
change Callback when the content of the input box changes (e: Event) => void
enter Callback for pressing Enter (e: Event) => void

The effect is as follows: online preview

insert image description here
insert image description here

Create input box component Input.vue

<script lang="ts">
/*
  一个根节点时,禁用组件根节点自动继承 attribute,必须使用这种写法!然后在要继承 attribute 的节点上绑定 v-bind="$attrs" 即可
  多个根节点时,只需在要继承 attribute 的节点上绑定 v-bind="$attrs" 即可
*/
export default {
    
    
  inheritAttrs: false
}
</script>
<script setup lang="ts">
import {
    
     ref, computed, onMounted } from 'vue'
interface Props {
    
    
  width?: string|number // 输入框宽度
  addonBefore?: string // 设置前置标签 string | slot
  addonAfter?: string // 设置后置标签 string | slot
  allowClear?: boolean // 可以点击清除图标删除内容
  password?: boolean // 是否启用密码框
  disabled?: boolean // 是否禁用
  maxlength?: number // 最大长度
  showCount?: boolean // 是否展示字数
  size?: 'large'|'middle'|'small' // 输入框大小
  prefix?: string // 前缀图标 string | slot
  suffix?: string // 后缀图标 string | slot
  value?: string // 输入框内容(v-model)
  valueModifiers?: object // 用于访问组件的v-model上添加的修饰符
}
const props = withDefaults(defineProps<Props>(), {
    
    
  width: '100%',
  addonBefore: '',
  addonAfter: '',
  allowClear: false,
  password: false,
  disabled: false,
  maxlength: undefined,
  showCount: false,
  size: 'middle',
  prefix: '',
  suffix: '',
  value: '',
  valueModifiers: () => ({
    
    })
})
const inputWidth = computed(() => {
    
    
  if (typeof props.width === 'number') {
    
    
    return props.width + 'px'
  }
  return props.width
})
const showCountNum = computed(() => {
    
    
  if (props.maxlength) {
    
    
    return props.value.length + ' / ' + props.maxlength
  }
  return props.value.length
})
const showPassword = ref(false)
const prefixRef = ref()
const showPrefix = ref(1)
const suffixRef = ref()
const showSuffix = ref(1)
const beforeRef = ref()
const showBefore = ref(1)
const afterRef = ref()
const showAfter = ref(1)
onMounted(() => {
    
    
  showPrefix.value = prefixRef.value.offsetWidth
  showSuffix.value = suffixRef.value.offsetWidth
  showBefore.value = beforeRef.value.offsetWidth
  showAfter.value = afterRef.value.offsetWidth
})
const emits = defineEmits(['update:value', 'change', 'enter'])
function onInput (e: any) {
    
    
  if (!('lazy' in props.valueModifiers)) {
    
    
    emits('update:value', e.target.value)
    emits('change', e)
  }
}
function onChange (e: any) {
    
    
  if ('lazy' in props.valueModifiers) {
    
    
    emits('update:value', e.target.value)
    emits('change', e)
  }
}
function onKeyboard (e: any) {
    
    
  if (e.key === 'Enter') {
    
    
    emits('enter', e)
  }
}
const input = ref()
function onClear () {
    
    
  emits('update:value', '')
  input.value.focus()
}
function onPassword () {
    
    
  showPassword.value = !showPassword.value
}
</script>
<template>
  <div class="m-input-wrap" :style="`width: ${inputWidth};`">
    <span class="m-addon" :class="{before: showBefore}" ref="beforeRef" v-if="showBefore!==23">
      <slot name="addonBefore">{
    
    {
    
     addonBefore }}</slot>
    </span>
    <div
      class="m-input"
      :class="[`${size}`, {disabled: disabled, 'input-before': showBefore!==23, 'input-after': showAfter!==23}]"
      tabindex="1">
      <span class="m-prefix" ref="prefixRef" v-if="showPrefix">
        <slot name="prefix">{
    
    {
    
     prefix }}</slot>
      </span>
      <input
        class="u-input"
        ref="input"
        :type="password && !showPassword ? 'password':'text'"
        :value="value"
        :maxlength="maxlength"
        :disabled="disabled"
        @input="onInput"
        @change="onChange"
        @keydown="onKeyboard"
        v-bind="$attrs" />
      <span class="m-suffix" ref="suffixRef" v-if="showSuffix">
        <span class="m-action" v-if="!disabled&&allowClear&&value" @click="onClear">
          <svg focusable="false" class="u-clear" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg>
        </span>
        <span class="m-action" v-if="password" @click="onPassword">
          <svg focusable="false" v-show="showPassword" class="u-eye" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path></svg>
          <svg focusable="false" v-show="!showPassword" class="u-eye" data-icon="eye-invisible" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M942.2 486.2Q889.47 375.11 816.7 305l-50.88 50.88C807.31 395.53 843.45 447.4 874.7 512 791.5 684.2 673.4 766 512 766q-72.67 0-133.87-22.38L323 798.75Q408 838 512 838q288.3 0 430.2-300.3a60.29 60.29 0 000-51.5zm-63.57-320.64L836 122.88a8 8 0 00-11.32 0L715.31 232.2Q624.86 186 512 186q-288.3 0-430.2 300.3a60.3 60.3 0 000 51.5q56.69 119.4 136.5 191.41L112.48 835a8 8 0 000 11.31L155.17 889a8 8 0 0011.31 0l712.15-712.12a8 8 0 000-11.32zM149.3 512C232.6 339.8 350.7 258 512 258c54.54 0 104.13 9.36 149.12 28.39l-70.3 70.3a176 176 0 00-238.13 238.13l-83.42 83.42C223.1 637.49 183.3 582.28 149.3 512zm246.7 0a112.11 112.11 0 01146.2-106.69L401.31 546.2A112 112 0 01396 512z"></path><path d="M508 624c-3.46 0-6.87-.16-10.25-.47l-52.82 52.82a176.09 176.09 0 00227.42-227.42l-52.82 52.82c.31 3.38.47 6.79.47 10.25a111.94 111.94 0 01-112 112z"></path></svg>
        </span>
        <span class="m-count" v-if="showCount">{
    
    {
    
     showCountNum }}</span>
        <slot name="suffix">{
    
    {
    
     suffix }}</slot>
      </span>
    </div>
    <span class="m-addon" :class="{after: showAfter}" ref="afterRef" v-if="showAfter!==23">
      <slot name="addonAfter">{
    
    {
    
     addonAfter }}</slot>
    </span>
  </div>
</template>
<style lang="less" scoped>
.m-input-wrap {
    
    
  width: 100%;
  text-align: start;
  vertical-align: top;
  position: relative;
  display: inline-table;
  border-collapse: separate;
  border-spacing: 0;
  .m-addon {
    
    
    position: relative;
    padding: 0 11px;
    color: rgba(0, 0, 0, 0.88);
    font-weight: normal;
    font-size: 14px;
    text-align: center;
    background-color: rgba(0, 0, 0, 0.02);
    border: 1px solid #d9d9d9;
    border-radius: 6px;
    transition: all 0.3s;
    line-height: 1;
    display: table-cell;
    width: 1px;
    white-space: nowrap;
    vertical-align: middle;
  }
  .before {
    
    
    border-start-end-radius: 0;
    border-end-end-radius: 0;
    border-inline-end: 0;
  }
  .after {
    
    
    border-start-start-radius: 0;
    border-end-start-radius: 0;
    border-inline-start: 0;
  }
  .m-input {
    
    
    font-size: 14px;
    color: rgba(0, 0, 0, 0.88);
    line-height: 1.5714285714285714;
    position: relative;
    display: inline-flex;
    width: 100%;
    min-width: 0;
    background-color: #ffffff;
    border: 1px solid #d9d9d9;
    transition: all 0.2s;
    &:hover {
    
    
      border-color: #4096ff;
      border-inline-end-width: 1px;
      z-index: 1;
    }
    &:focus-within {
    
    
      border-color: #4096ff;
      box-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1);
      border-inline-end-width: 1px;
      outline: 0;
    }
    .m-prefix {
    
    
      margin-inline-end: 4px;
      display: flex;
      flex: none;
      align-items: center;
    }
    .u-input {
    
    
      font-size: 14px;
      line-height: 1.5714285714285714;
      position: relative;
      display: inline-block;
      width: 100%;
      min-width: 0;
      background-color: #ffffff;
      border: none;
      outline: none;
      text-overflow: ellipsis;
      transition: all 0.2s;
    }
    input:disabled {
    
    
      color: rgba(0, 0, 0, 0.25);
    }
    input::-webkit-input-placeholder {
    
    
      color: rgba(0, 0, 0, 0.25)
    }
    input:-moz-placeholder {
    
    
      color: rgba(0, 0, 0, 0.25)
    }
    input::-moz-placeholder {
    
    
      color: rgba(0, 0, 0, 0.25)
    }
    input:-ms-input-placeholder {
    
    
      color: rgba(0, 0, 0, 0.25)
    }
    .m-suffix {
    
    
      margin-inline-start: 4px;
      display: flex;
      flex: none;
      align-items: center;
      span {
    
    
        margin-right: 4px;
      }
      .m-action {
    
    
        cursor: pointer;
        .u-clear {
    
    
          font-size: 12px;
          display: inline-block;
          fill: rgba(0, 0, 0, 0.25);
          text-align: center;
          line-height: 0;
          vertical-align: -0.08em;
          transition: fill 0.3s;
          &:hover {
    
    
            fill: rgba(0, 0, 0, 0.45);
          }
        }
        .u-eye {
    
    
          font-size: 14px;
          display: inline-block;
          fill: rgba(0, 0, 0, 0.45);
          text-align: center;
          line-height: 1;
          vertical-align: -0.125em;
          transition: fill 0.3s;
          &:hover {
    
    
            fill: rgba(0, 0, 0, 0.85);
          }
        }
      }
      .m-count {
    
    
        color: rgba(0, 0, 0, 0.45);
      }
    }
  }
  .large {
    
    
    padding: 7px 11px;
    font-size: 16px;
    line-height: 1.5;
    border-radius: 8px;
  }
  .middle {
    
    
    padding: 4px 11px;
    border-radius: 6px;
  }
  .small {
    
    
    padding: 0px 7px;
    border-radius: 4px;
  }
  .input-before {
    
    
    border-start-start-radius: 0;
    border-end-start-radius: 0;
  }
  .input-after {
    
    
    border-start-end-radius: 0;
    border-end-end-radius: 0;
  }
  .disabled {
    
    
    color: rgba(0, 0, 0, 0.25);
    background-color: rgba(0, 0, 0, 0.04);
    cursor: not-allowed;
    &:hover {
    
    
      border-color: #d9d9d9;
    }
    &:focus-within {
    
    
      border-color: #d9d9d9;
      box-shadow: none
    }
    .u-input {
    
    
      background-color: transparent;
      cursor: not-allowed;
    }
  }
}
</style>

Introduce in the page to be used

Which introduces the use of Vue3 spacing (Space)

<script setup lang="ts">
import Input from './Input.vue'
import {
    
     ref, watchEffect } from 'vue'
const value = ref('')
const lazyValue = ref('')
watchEffect(() => {
    
    
  console.log('value:', value.value)
})
watchEffect(() => {
    
    
  console.log('lazyValue:', lazyValue.value)
})
function onChange (e: Event) {
    
    
  console.log('change e:', e)
}
function onEnter (e: KeyboardEvent) {
    
    
  console.log('enter e:', e)
}
</script>
<template>
  <div>
    <h1>Input 输入框</h1>
    <h2 class="mt30 mb10">基本使用</h2>
    <Space direction="vertical">
      <Input
        v-model:value="value"
        placeholder="Basic usage"
        @change="onChange"
        @enter="onEnter" />
      <Input
        v-model:value.lazy="lazyValue"
        placeholder="Lazy usage"
        @change="onChange" />
    </Space>
    <h2 class="mt30 mb10">前缀和后缀</h2>
    <Space direction="vertical">
      <Input v-model:value="value" placeholder="Basic usage">
        <template #prefix>
          <svg focusable="false" class="u-svg" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg>
        </template>
        <template #suffix>
          <Tooltip :max-width="150" title="Extra information">
            <svg focusable="false" class="u-svg" data-icon="info-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path><path d="M464 336a48 48 0 1096 0 48 48 0 10-96 0zm72 112h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V456c0-4.4-3.6-8-8-8z"></path></svg>
          </Tooltip>
        </template>
      </Input>
      <Input v-model:value="value" prefix="¥" suffix="RMB" />
    </Space>
    <h2 class="mt30 mb10">三种大小</h2>
    <Space direction="vertical">
      <Input
        size="large"
        :width="500"
        show-count
        :maxlength="20"
        allow-clear
        v-model:value="value"
        placeholder="large size"
        prefix="¥"
        suffix="RMB"
        addon-before="Http://"
        addon-after=".com" />
      <Input
        :width="500"
        show-count
        :maxlength="20"
        allow-clear
        v-model:value="value"
        placeholder="default size"
        prefix="¥"
        suffix="RMB"
        addon-before="Http://"
        addon-after=".com" />
      <Input
        size="small"
        :width="500"
        show-count
        :maxlength="20"
        allow-clear
        v-model:value="value"
        placeholder="small size"
        prefix="¥"
        suffix="RMB"
        addon-before="Http://"
        addon-after=".com" />
    </Space>
    <h2 class="mt30 mb10">前置/后置标签</h2>
    <Space direction="vertical">
      <Input
        :width="300"
        disabled
        show-count
        :maxlength="20"
        v-model:value="value"
        placeholder="Basic usage"
        prefix="¥"
        suffix="RMB"
        addon-before="Http://"
        addon-after=".com" />
      <Input v-model:value="value">
        <template #addonAfter>
          <svg focusable="false" class="u-svg" data-icon="setting" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56a32.03 32.03 0 009.3-35.2l-.9-2.6a443.74 443.74 0 00-79.7-137.9l-1.8-2.1a32.12 32.12 0 00-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85a32.05 32.05 0 00-25.8-25.7l-2.7-.5c-52.1-9.4-106.9-9.4-159 0l-2.7.5a32.05 32.05 0 00-25.8 25.7l-15.8 85.4a351.86 351.86 0 00-99 57.4l-81.9-29.1a32 32 0 00-35.1 9.5l-1.8 2.1a446.02 446.02 0 00-79.7 137.9l-.9 2.6c-4.5 12.5-.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5a32.03 32.03 0 00-9.3 35.2l.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1a32.12 32.12 0 0035.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4a32.05 32.05 0 0025.8 25.7l2.7.5a449.4 449.4 0 00159 0l2.7-.5a32.05 32.05 0 0025.8-25.7l15.7-85a350 350 0 0099.7-57.6l81.3 28.9a32 32 0 0035.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l.9-2.6c4.5-12.3.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9a370.03 370.03 0 01-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97a377.5 377.5 0 01-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9zM512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm79.2 255.2A111.6 111.6 0 01512 614c-29.9 0-58-11.7-79.2-32.8A111.6 111.6 0 01400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8A111.6 111.6 0 01624 502c0 29.9-11.7 58-32.8 79.2z"></path></svg>
        </template>
      </Input>
    </Space>
    <h2 class="mt30 mb10">带移除图标</h2>
    <Space>
      <Input allow-clear v-model:value="value" placeholder="input with clear icon" suffix="RMB" />
    </Space>
    <h2 class="mt30 mb10">密码框</h2>
    <Space>
      <Input password allow-clear v-model:value="value" suffix="RMB" placeholder="input password" />
    </Space>
    <h2 class="mt30 mb10">带数字提示</h2>
    <Space>
      <Input show-count allow-clear v-model:value="value" suffix="RMB"/>
    </Space>
    <h2 class="mt30 mb10">禁用</h2>
    <Space>
      <Input disabled v-model:value="value" suffix="RMB"/>
    </Space>
  </div>
</template>
<style lang="less" scoped>
.u-svg {
    
    
  display: inline-flex;
  align-items: center;
  line-height: 0;
  text-align: center;
  vertical-align: -0.125em;
  fill: rgba(0, 0, 0, 0.88);
}
</style>

Guess you like

Origin blog.csdn.net/Dandrose/article/details/131922317