207 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
<script lang="ts">
 | 
						|
export default {
 | 
						|
  name: "LayInput",
 | 
						|
};
 | 
						|
</script>
 | 
						|
 | 
						|
<script setup lang="ts">
 | 
						|
import "./index.less";
 | 
						|
import { LayIcon } from "@layui/icons-vue";
 | 
						|
import { computed, ref, useSlots, watch } from "vue";
 | 
						|
import PasswordIcon from "./svg/Password.vue";
 | 
						|
import UnPasswordIcon from "./svg/unPassword.vue";
 | 
						|
import { InputSize } from "./interface";
 | 
						|
 | 
						|
export interface InputProps {
 | 
						|
  name?: string;
 | 
						|
  type?: string;
 | 
						|
  prefixIcon?: string;
 | 
						|
  suffixIcon?: string;
 | 
						|
  modelValue?: string | number | string[] | undefined;
 | 
						|
  allowClear?: boolean;
 | 
						|
  autocomplete?: string;
 | 
						|
  placeholder?: string;
 | 
						|
  autofocus?: boolean;
 | 
						|
  disabled?: boolean;
 | 
						|
  readonly?: boolean;
 | 
						|
  password?: boolean;
 | 
						|
  size?: InputSize;
 | 
						|
  maxlength?: number;
 | 
						|
  max?: number;
 | 
						|
  min?: number;
 | 
						|
}
 | 
						|
 | 
						|
const props = withDefaults(defineProps<InputProps>(), {
 | 
						|
  disabled: false,
 | 
						|
  readonly: false,
 | 
						|
  allowClear: false,
 | 
						|
  autofocus: false,
 | 
						|
  password: false,
 | 
						|
  modelValue: "",
 | 
						|
  size: "md",
 | 
						|
});
 | 
						|
 | 
						|
interface InputEmits {
 | 
						|
  (e: "blur", event: Event): void;
 | 
						|
  (e: "input", value: string): void;
 | 
						|
  (e: "update:modelValue", value: string): void;
 | 
						|
  (e: "change", value: string): void;
 | 
						|
  (e: "focus", event: Event): void;
 | 
						|
  (e: "clear"): void;
 | 
						|
}
 | 
						|
 | 
						|
const emit = defineEmits<InputEmits>();
 | 
						|
 | 
						|
const slots = useSlots();
 | 
						|
const type = ref(props.type);
 | 
						|
const currentValue = ref<string>(
 | 
						|
  String(props.modelValue == null ? "" : props.modelValue)
 | 
						|
);
 | 
						|
const hasContent = computed(() => (props.modelValue as string)?.length > 0);
 | 
						|
const isPassword = computed(() => type.value == "password");
 | 
						|
const composing = ref(false);
 | 
						|
 | 
						|
watch(
 | 
						|
  () => props.type,
 | 
						|
  () => {
 | 
						|
    type.value = props.type;
 | 
						|
  }
 | 
						|
);
 | 
						|
 | 
						|
watch(
 | 
						|
  () => props.modelValue,
 | 
						|
  () => {
 | 
						|
    currentValue.value = String(
 | 
						|
      props.modelValue == null ? "" : props.modelValue
 | 
						|
    );
 | 
						|
  }
 | 
						|
);
 | 
						|
 | 
						|
const onInput = function (event: Event) {
 | 
						|
  const inputElement = event.target as HTMLInputElement;
 | 
						|
  const value = inputElement.value;
 | 
						|
  emit("input", value);
 | 
						|
  if (composing.value) return;
 | 
						|
  emit("update:modelValue", value);
 | 
						|
};
 | 
						|
 | 
						|
const onClear = () => {
 | 
						|
  emit("update:modelValue", "");
 | 
						|
  emit("clear");
 | 
						|
};
 | 
						|
 | 
						|
const onFocus = (event: Event) => {
 | 
						|
  emit("focus", event);
 | 
						|
};
 | 
						|
 | 
						|
const onChange = (event: Event) => {
 | 
						|
  const inputElement = event.target as HTMLInputElement;
 | 
						|
  const value = inputElement.value;
 | 
						|
  emit("change", value);
 | 
						|
};
 | 
						|
 | 
						|
const onBlur = (event: Event) => {
 | 
						|
  if (props.type === "number") {
 | 
						|
    onNumberBlur(event);
 | 
						|
  }
 | 
						|
  emit("blur", event);
 | 
						|
};
 | 
						|
 | 
						|
const onNumberBlur = (event: Event) => {
 | 
						|
  let value = (event.target as HTMLInputElement).value;
 | 
						|
  if (value === "") {
 | 
						|
    value = props.min ? String(props.min) : "0";
 | 
						|
  } else {
 | 
						|
    if (props.max && props.max < Number(value)) value = props.max.toString();
 | 
						|
    if (props.min && props.min > Number(value)) value = props.min.toString();
 | 
						|
  }
 | 
						|
  emit("update:modelValue", value);
 | 
						|
};
 | 
						|
 | 
						|
const onCompositionstart = () => {
 | 
						|
  composing.value = true;
 | 
						|
};
 | 
						|
 | 
						|
const onCompositionend = (event: Event) => {
 | 
						|
  composing.value = false;
 | 
						|
  onInput(event);
 | 
						|
};
 | 
						|
 | 
						|
const classes = computed(() => {
 | 
						|
  return {
 | 
						|
    "layui-input-disabled": props.disabled,
 | 
						|
    "layui-input-has-prefix": slots.prefix || props.prefixIcon,
 | 
						|
  };
 | 
						|
});
 | 
						|
 | 
						|
const showPassword = () => {
 | 
						|
  if (isPassword.value) {
 | 
						|
    type.value = "text";
 | 
						|
  } else {
 | 
						|
    type.value = "password";
 | 
						|
  }
 | 
						|
};
 | 
						|
</script>
 | 
						|
 | 
						|
<template>
 | 
						|
  <div class="layui-input" :class="classes" :size="size">
 | 
						|
    <div class="layui-input-prepend" v-if="slots.prepend">
 | 
						|
      <slot name="prepend"></slot>
 | 
						|
    </div>
 | 
						|
    <div class="layui-input-wrapper">
 | 
						|
      <span class="layui-input-prefix" v-if="slots.prefix || props.prefixIcon">
 | 
						|
        <slot name="prefix" v-if="slots.prefix"></slot>
 | 
						|
        <lay-icon
 | 
						|
          v-else
 | 
						|
          :type="props.prefixIcon"
 | 
						|
          class="layui-input-prefix-icon"
 | 
						|
        ></lay-icon>
 | 
						|
      </span>
 | 
						|
      <input
 | 
						|
        :type="type"
 | 
						|
        :name="name"
 | 
						|
        :disabled="disabled"
 | 
						|
        :placeholder="placeholder"
 | 
						|
        :autofocus="autofocus"
 | 
						|
        :autocomplete="autocomplete"
 | 
						|
        :maxlength="maxlength"
 | 
						|
        :max="max"
 | 
						|
        :min="min"
 | 
						|
        :readonly="readonly"
 | 
						|
        :value="currentValue"
 | 
						|
        @input="onInput"
 | 
						|
        @change="onChange"
 | 
						|
        @focus="onFocus"
 | 
						|
        @blur="onBlur"
 | 
						|
        @compositionstart="onCompositionstart"
 | 
						|
        @compositionend="onCompositionend"
 | 
						|
      />
 | 
						|
      <span
 | 
						|
        class="layui-input-password"
 | 
						|
        @click="showPassword"
 | 
						|
        v-if="password && hasContent"
 | 
						|
      >
 | 
						|
        <password-icon v-if="isPassword"></password-icon>
 | 
						|
        <un-password-icon v-else></un-password-icon>
 | 
						|
      </span>
 | 
						|
      <span
 | 
						|
        class="layui-input-clear"
 | 
						|
        v-if="allowClear && hasContent && !disabled"
 | 
						|
      >
 | 
						|
        <lay-icon type="layui-icon-close-fill" @click.stop="onClear"></lay-icon>
 | 
						|
      </span>
 | 
						|
      <span class="layui-input-suffix" v-if="slots.suffix || props.suffixIcon">
 | 
						|
        <slot name="suffix" v-if="slots.suffix"></slot>
 | 
						|
        <lay-icon
 | 
						|
          v-else
 | 
						|
          :type="props.suffixIcon"
 | 
						|
          class="layui-input-suffix-icon"
 | 
						|
        ></lay-icon>
 | 
						|
      </span>
 | 
						|
    </div>
 | 
						|
    <div class="layui-input-append" v-if="slots.append">
 | 
						|
      <slot name="append"></slot>
 | 
						|
    </div>
 | 
						|
  </div>
 | 
						|
</template>
 |