(component): [tagInput]添加事件

This commit is contained in:
sight 2022-09-26 20:44:48 +08:00
parent 4e9604377b
commit af86d77e17
2 changed files with 99 additions and 33 deletions

View File

@ -16,12 +16,7 @@ import {
reactive, reactive,
nextTick, nextTick,
} from "vue"; } from "vue";
import { import { isObject, reactiveOmit, useResizeObserver } from "@vueuse/core";
isObject,
reactiveOmit,
useResizeObserver,
useVModel,
} from "@vueuse/core";
import { LayIcon } from "@layui/icons-vue"; import { LayIcon } from "@layui/icons-vue";
export interface TagData { export interface TagData {
@ -31,7 +26,7 @@ export interface TagData {
[other: string]: any; [other: string]: any;
} }
export interface LayInputTagProps { export interface LayTagInputProps {
modelValue?: (string | number | TagData)[]; modelValue?: (string | number | TagData)[];
inputValue?: string; inputValue?: string;
disabled?: boolean; disabled?: boolean;
@ -46,13 +41,23 @@ export interface LayInputTagProps {
disabledInput?: boolean; disabledInput?: boolean;
} }
const props = withDefaults(defineProps<LayInputTagProps>(), { const props = withDefaults(defineProps<LayTagInputProps>(), {
placeholder: undefined, placeholder: undefined,
minCollapsedNum: 0, minCollapsedNum: 0,
size: "md", size: "md",
}); });
const emit = defineEmits(["update:modelValue", "update:inputValue"]); const emit = defineEmits([
"update:modelValue",
"update:inputValue",
"change",
"inputValueChange",
"remove",
"clear",
"focus",
"blur",
"pressEnter",
]);
const mirrorRefEl = shallowRef<HTMLElement | undefined>(undefined); const mirrorRefEl = shallowRef<HTMLElement | undefined>(undefined);
const inputRefEl = shallowRef<HTMLInputElement | undefined>(undefined); const inputRefEl = shallowRef<HTMLInputElement | undefined>(undefined);
@ -60,13 +65,27 @@ const oldInputValue = ref<string>("");
const compositionValue = ref<string>(""); const compositionValue = ref<string>("");
const isComposing = ref(false); const isComposing = ref(false);
const inputStyle = reactive({ width: "15px" }); const inputStyle = reactive({ width: "15px" });
const inputValue = useVModel(props, "inputValue", emit, { defaultValue: "" });
const tagData = useVModel(props, "modelValue", emit, {
deep: true,
defaultValue: [] as TagData[],
});
const _tagProps = reactive(props.tagProps ?? {}); const _tagProps = reactive(props.tagProps ?? {});
const tagProps = reactiveOmit(_tagProps, "closable", "size", "disabled"); const tagProps = reactiveOmit(_tagProps, "closable", "size", "disabled");
const inputValue = computed({
get() {
return props.inputValue;
},
set(val) {
emit("update:inputValue", val);
emit("inputValueChange", val);
},
});
const tagData = computed({
get() {
return props.modelValue;
},
set(val) {
emit("update:modelValue", val);
emit("change", val);
},
});
const normalizedTags = computed(() => normalizedTagData(tagData.value ?? [])); const normalizedTags = computed(() => normalizedTagData(tagData.value ?? []));
@ -85,26 +104,26 @@ const collapsedTagData = computed(() => {
: []; : [];
}); });
const handleInput = function (event: Event) { const handleInput = function (e: Event) {
if (isComposing.value) { if (isComposing.value) {
return; return;
} }
inputValue.value = (event.target as HTMLInputElement).value; inputValue.value = (e.target as HTMLInputElement).value;
}; };
const handleComposition = (event: CompositionEvent) => { const handleComposition = (e: CompositionEvent) => {
if (event.type === "compositionend") { if (e.type === "compositionend") {
isComposing.value = false; isComposing.value = false;
compositionValue.value = ""; compositionValue.value = "";
handleInput(event); handleInput(e);
} else { } else {
isComposing.value = true; isComposing.value = true;
compositionValue.value = inputValue.value + (event.data ?? ""); compositionValue.value = inputValue.value + (e.data ?? "");
} }
}; };
const handleEnter = (event: KeyboardEvent) => { const handleEnter = (e: KeyboardEvent) => {
event.preventDefault(); e.preventDefault();
const valueStr = inputValue.value ? String(inputValue.value).trim() : ""; const valueStr = inputValue.value ? String(inputValue.value).trim() : "";
if (!valueStr || !tagData.value) return; if (!valueStr || !tagData.value) return;
const isLimit = props.max && tagData.value?.length >= props.max; const isLimit = props.max && tagData.value?.length >= props.max;
@ -115,21 +134,25 @@ const handleEnter = (event: KeyboardEvent) => {
: [valueStr]; : [valueStr];
inputValue.value = ""; inputValue.value = "";
} }
emit("pressEnter", inputValue.value, e);
}; };
const handleBackspaceKeyUp = (event: KeyboardEvent) => { const handleBackspaceKeyUp = (e: KeyboardEvent) => {
if (!tagData.value || !tagData.value.length) return; if (!tagData.value || !tagData.value.length) return;
if (!oldInputValue.value && ["Backspace", "Delete"].includes(event.code)) { if (!oldInputValue.value && ["Backspace", "Delete"].includes(e.code)) {
tagData.value = tagData.value.slice(0, -1); const lastIndex = normalizedTags.value.length - 1;
handleClose(normalizedTags.value[lastIndex].value, lastIndex, e);
} }
oldInputValue.value = inputValue.value ?? ""; oldInputValue.value = inputValue.value ?? "";
}; };
const handleFocus = () => { const handleFocus = (e: FocusEvent) => {
emit("focus", e);
(inputRefEl.value as HTMLInputElement)?.focus(); (inputRefEl.value as HTMLInputElement)?.focus();
}; };
const handleBlur = () => { const handleBlur = (e: FocusEvent) => {
emit("blur", e);
(inputRefEl.value as HTMLInputElement)?.blur(); (inputRefEl.value as HTMLInputElement)?.blur();
}; };
@ -138,13 +161,26 @@ const handleClearClick = (e: MouseEvent) => {
return; return;
} }
tagData.value = []; tagData.value = [];
emit("clear", e);
}; };
const handleClose = (index: number) => { const handleClose = (
value: string | number | undefined,
index: number,
e: Event
) => {
if (!tagData.value) return; if (!tagData.value) return;
const arr = [...tagData.value]; const arr = [...tagData.value];
arr.splice(index, 1); arr.splice(index, 1);
tagData.value = arr; tagData.value = arr;
emit("remove", value, e);
};
const handleMouseDown = (e: MouseEvent) => {
if (inputRefEl.value) {
e.preventDefault();
inputRefEl.value.focus();
}
}; };
const setInputWidth = (width: number) => { const setInputWidth = (width: number) => {
@ -210,7 +246,7 @@ defineExpose({
}); });
</script> </script>
<template> <template>
<div :class="cls" @click="handleFocus"> <div :class="cls" @mousedown="handleMouseDown">
<span ref="mirrorRefEl" class="layui-tag-input-mirror"> <span ref="mirrorRefEl" class="layui-tag-input-mirror">
{{ compositionValue || inputValue || placeholder }} {{ compositionValue || inputValue || placeholder }}
</span> </span>
@ -226,7 +262,7 @@ defineExpose({
v-bind="tagProps" v-bind="tagProps"
:closable="!readonly && !disabled && item.closable" :closable="!readonly && !disabled && item.closable"
:size="size" :size="size"
@close="handleClose(index)" @close="handleClose(item.value, index, $event)"
> >
{{ item.label }} {{ item.label }}
</LayTag> </LayTag>
@ -249,7 +285,13 @@ defineExpose({
v-bind="tagProps" v-bind="tagProps"
:closable="!readonly && !disabled && item.closable" :closable="!readonly && !disabled && item.closable"
:size="size" :size="size"
@close="handleClose(index + (minCollapsedNum ?? 0))" @close="
handleClose(
item.value,
index + (minCollapsedNum ?? 0),
$event
)
"
> >
{{ item.label }} {{ item.label }}
</LayTag> </LayTag>
@ -268,6 +310,8 @@ defineExpose({
@keydown.enter="handleEnter" @keydown.enter="handleEnter"
@keyup="handleBackspaceKeyUp" @keyup="handleBackspaceKeyUp"
@input="handleInput" @input="handleInput"
@focus="handleFocus"
@blur="handleBlur"
@compositionstart="handleComposition" @compositionstart="handleComposition"
@compositionupdate="handleComposition" @compositionupdate="handleComposition"
@compositionend="handleComposition" @compositionend="handleComposition"

View File

@ -13,7 +13,14 @@
::: demo 使用 `lay-tag-input` 标签, 创建标签输入框。 ::: demo 使用 `lay-tag-input` 标签, 创建标签输入框。
<template> <template>
<lay-tag-input v-model="data1" v-model:inputValue="inputValue1" placeholder="请输入" style="width:320px"></lay-tag-input> <lay-tag-input
v-model="data1"
v-model:inputValue="inputValue1"
allowClear
placeholder="请输入"
style="width:320px"
>
</lay-tag-input>
</template> </template>
<script> <script>
@ -220,7 +227,7 @@ export default {
| size | 输入框大小 |`string` | `md` | `lg` `md` `sm` `xs`| | size | 输入框大小 |`string` | `md` | `lg` `md` `sm` `xs`|
| inputValue | 输入框的值 | `string` | -| - | | inputValue | 输入框的值 | `string` | -| - |
| placeholder | 占位符 | `string` | - | - | | placeholder | 占位符 | `string` | - | - |
| readonly | 是否可关闭 | `boolean` | `false` | `true` `false`| | readonly | 是否可只读 | `boolean` | `false` | `true` `false`|
| allowClear | 允许清空 | `boolean` | `false` | `true` `false`| | allowClear | 允许清空 | `boolean` | `false` | `true` `false`|
| disabled | 是否禁用 | `boolean` | `false` | `true` `false`| | disabled | 是否禁用 | `boolean` | `false` | `true` `false`|
| max | 最大输入标签数量 | `number` | -|-| | max | 最大输入标签数量 | `number` | -|-|
@ -231,6 +238,21 @@ export default {
::: :::
:::title TagInput 事件
:::
:::table
| 名称 | 描述 | 参数 |
|------ |----------|-----------|
| change | 值改变时触发 | (value: (string &#124; number &#124; TagData)[]) |
| inputValueChange |输入框值改变时触发 | (inputValue: string) |
| remove|删除标签时触发| (value: string &#124; number, e: Event) |
| clear | 点击清除按钮时触发| (e: Event) |
| focus | 获得焦点时触发| (e: Event) |
| blur | 失去焦点时触发| (e: Event) |
| pressEnter| 按下 Enter 键时触发| (inputValue: string, e: Event) |
:::
:::title TagInput 方法 :::title TagInput 方法
::: :::