|
4 | 4 | <lay-input
|
5 | 5 | :name="name"
|
6 | 6 | :model-value="innerValue"
|
7 |
| - :placeholder="placeholder" |
8 | 7 | :allow-clear="allowClear"
|
| 8 | + :placeholder="placeholder" |
| 9 | + @click="clickHandler" |
9 | 10 | @input="inputHandler"
|
| 11 | + @blur="blurHandler" |
| 12 | + @focus="focusHandler" |
| 13 | + @compositionstart="onCompositionstart" |
| 14 | + @compositionend="onCompositionend" |
10 | 15 | ></lay-input>
|
11 | 16 | <template #content>
|
12 | 17 | <template v-if="innerOptions.length > 0">
|
13 | 18 | <lay-dropdown-menu>
|
14 | 19 | <template v-for="(option, index) in innerOptions">
|
15 | 20 | <lay-dropdown-menu-item
|
16 |
| - class="lay-autocomplete-option" |
17 |
| - :class="{ |
| 21 | + @click="clickOptions(option)" |
| 22 | + :class="['lay-autocomplete-option', { |
18 | 23 | selected: selectedIndex == index,
|
19 | 24 | equals: innerValue == option,
|
20 |
| - }" |
| 25 | + }]" |
21 | 26 | >
|
22 | 27 | {{ option }}
|
23 | 28 | </lay-dropdown-menu-item>
|
24 | 29 | </template>
|
25 | 30 | </lay-dropdown-menu>
|
26 | 31 | </template>
|
27 |
| - <template v-else> |
28 |
| - <div class="lay-autocomplete-empty">暂无内容</div> |
29 |
| - </template> |
30 | 32 | </template>
|
31 | 33 | </lay-dropdown>
|
32 | 34 | </div>
|
33 | 35 | </template>
|
34 | 36 |
|
35 | 37 | <script lang="ts">
|
36 | 38 | export default {
|
37 |
| - name: "LayAutoComplete", |
| 39 | + name: "LayAutocomplete", |
38 | 40 | };
|
39 | 41 | </script>
|
40 | 42 |
|
41 | 43 | <script lang="ts" setup>
|
42 |
| -import { ref, watch, reactive, onMounted, onUnmounted } from "vue"; |
| 44 | +import { ref, watch, onMounted, onUnmounted, nextTick } from "vue"; |
43 | 45 |
|
44 |
| -export interface AvatarProps { |
45 |
| - modalValue?: string; |
46 |
| - options?: string[]; |
| 46 | +export interface AutocompleteProps { |
| 47 | + modelValue: string; |
| 48 | + fetchSuggestions: Function; |
47 | 49 | placeholder?: string;
|
48 | 50 | allowClear?: boolean;
|
49 | 51 | name?: string;
|
50 | 52 | }
|
51 | 53 |
|
52 |
| -const props = withDefaults(defineProps<AvatarProps>(), {}); |
| 54 | +const props = withDefaults(defineProps<AutocompleteProps>(), {}); |
| 55 | +
|
| 56 | +interface AutocompleteEmits { |
| 57 | + (e: "update:modelValue", value: string): void; |
| 58 | +} |
| 59 | +
|
| 60 | +const emits = defineEmits<AutocompleteEmits>(); |
53 | 61 |
|
54 |
| -const innerValue = ref(props.modalValue); |
55 |
| -const innerOptions = reactive<string[]>([]); |
| 62 | +const isFocus = ref(false); |
| 63 | +const innerValue = ref(props.modelValue); |
| 64 | +const innerOptions = ref<string[]>([]); |
| 65 | +const selectedIndex = ref(-1); |
56 | 66 | const dropdownRef = ref();
|
57 |
| -const selectedIndex = ref(); |
| 67 | +const composing = ref(false); |
| 68 | +
|
| 69 | +const onCompositionstart = () => { |
| 70 | + composing.value = true; |
| 71 | +}; |
| 72 | +
|
| 73 | +const onCompositionend = (eventParam: Event) => { |
| 74 | + composing.value = false; |
| 75 | + inputHandler((eventParam.target as HTMLInputElement).value); |
| 76 | +}; |
58 | 77 |
|
59 | 78 | watch(
|
60 |
| - () => props.modalValue, |
| 79 | + () => props.modelValue, |
61 | 80 | () => {
|
62 |
| - innerValue.value = props.modalValue; |
| 81 | + innerValue.value = props.modelValue; |
63 | 82 | }
|
64 | 83 | );
|
65 | 84 |
|
66 | 85 | const inputHandler = function (value: string) {
|
67 |
| - innerValue.value = value; |
68 |
| - dropdownRef.value.show(); |
| 86 | + if (!composing.value) { |
| 87 | + emits("update:modelValue", value); |
| 88 | + props.fetchSuggestions(value).then((suggestions: any[]) => { |
| 89 | + innerOptions.value = suggestions || []; |
| 90 | + }) |
| 91 | + } |
69 | 92 | };
|
70 | 93 |
|
71 |
| -watch([innerValue, props.options], () => { |
72 |
| - innerOptions.splice(0); |
73 |
| - props.options?.forEach((option) => { |
74 |
| - if (innerValue.value && option.indexOf(innerValue.value) != -1) { |
75 |
| - innerOptions.push(option); |
76 |
| - } |
| 94 | +const clickHandler = function () { |
| 95 | + nextTick(() => { |
| 96 | + dropdownRef.value.hide(); |
77 | 97 | });
|
78 |
| - if (innerOptions.length > 0) { |
79 |
| - selectedIndex.value = 0; |
| 98 | +}; |
| 99 | +
|
| 100 | +const clickOptions = function (value: string) { |
| 101 | + innerValue.value = value; |
| 102 | +} |
| 103 | +
|
| 104 | +const blurHandler = function () { |
| 105 | + isFocus.value = false; |
| 106 | +}; |
| 107 | +
|
| 108 | +const focusHandler = function () { |
| 109 | + isFocus.value = true; |
| 110 | +}; |
| 111 | +
|
| 112 | +watch([innerValue, innerOptions], () => { |
| 113 | + let isEquals = false; |
| 114 | + if (innerValue.value != undefined && innerValue.value != "") { |
| 115 | + innerOptions.value.forEach((option, index) => { |
| 116 | + if (innerValue.value === option) { |
| 117 | + selectedIndex.value = index; |
| 118 | + isEquals = true; |
| 119 | + } |
| 120 | + }); |
| 121 | + } |
| 122 | + if (isEquals === false) { |
| 123 | + selectedIndex.value = -1; |
80 | 124 | }
|
81 | 125 | });
|
82 | 126 |
|
| 127 | +watch(innerOptions, () => { |
| 128 | + if(innerOptions.value.length > 0) { |
| 129 | + dropdownRef.value.show(); |
| 130 | + } else { |
| 131 | + dropdownRef.value.hide(); |
| 132 | + } |
| 133 | +}) |
| 134 | +
|
83 | 135 | onMounted(() => {
|
84 | 136 | document.addEventListener("keyup", function (e) {
|
85 |
| - if (e.key === "ArrowUp") { |
86 |
| - if (selectedIndex.value > 0) { |
87 |
| - selectedIndex.value = selectedIndex.value - 1; |
| 137 | + if (isFocus.value === true) { |
| 138 | + if (e.key === "ArrowDown") { |
| 139 | + if (selectedIndex.value <= innerOptions.value.length - 2) { |
| 140 | + selectedIndex.value++; |
| 141 | + } |
88 | 142 | }
|
89 |
| - } |
90 |
| - if (e.key === "ArrowDown") { |
91 |
| - if (selectedIndex.value <= innerOptions.length - 2) { |
92 |
| - selectedIndex.value = selectedIndex.value + 1; |
| 143 | + if (e.key === "ArrowUp") { |
| 144 | + if (selectedIndex.value > 0) { |
| 145 | + selectedIndex.value--; |
| 146 | + } |
| 147 | + } |
| 148 | + if (e.key === "Enter") { |
| 149 | + innerValue.value = innerOptions.value[selectedIndex.value]; |
| 150 | + dropdownRef.value.hide(); |
93 | 151 | }
|
94 |
| - } |
95 |
| - if (e.key === "Enter") { |
96 |
| - innerValue.value = innerOptions[selectedIndex.value]; |
97 |
| - dropdownRef.value.hide(); |
98 | 152 | }
|
99 | 153 | });
|
100 | 154 | });
|
|
0 commit comments