@veltra/desktop 1.3.5 → 1.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/components/action/action-group.vue.d.ts +1 -1
  2. package/dist/components/action/action.vue.d.ts +1 -1
  3. package/dist/components/cascade/cascade.js +8 -2
  4. package/dist/components/cascade/cascade.js.map +1 -1
  5. package/dist/components/cascade/style2.css +4 -0
  6. package/dist/components/date-range-picker/date-range-picker.js +5 -2
  7. package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
  8. package/dist/components/date-range-picker/style2.css +4 -0
  9. package/dist/components/empty/empty.vue.d.ts +1 -1
  10. package/dist/components/form-item/form-item.js.map +1 -1
  11. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js +2 -2
  12. package/dist/components/form-item/form-item.vue_vue_type_script_setup_true_lang.js.map +1 -1
  13. package/dist/components/form-item/style2.css +2 -1
  14. package/dist/components/grid-input/style2.css +3 -0
  15. package/dist/components/multi-select/multi-select.js +67 -40
  16. package/dist/components/multi-select/multi-select.js.map +1 -1
  17. package/dist/components/multi-select/multi-select.vue.d.ts +4 -4
  18. package/dist/components/multi-select/style2.css +23 -10
  19. package/dist/components/multi-tree-select/multi-tree-select.js +52 -35
  20. package/dist/components/multi-tree-select/multi-tree-select.js.map +1 -1
  21. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +1 -1
  22. package/dist/components/multi-tree-select/style2.css +26 -16
  23. package/dist/components/progress-nodes/progress-nodes.vue.d.ts +1 -1
  24. package/dist/components/select/select.js +159 -95
  25. package/dist/components/select/select.js.map +1 -1
  26. package/dist/components/select/style2.css +4 -17
  27. package/dist/components/select/use-options.js +67 -26
  28. package/dist/components/select/use-options.js.map +1 -1
  29. package/dist/components/steps/steps.vue.d.ts +2 -2
  30. package/dist/components/tabs/tabs-horizontal.vue.d.ts +2 -2
  31. package/dist/components/tabs/tabs-vertical.vue.d.ts +2 -2
  32. package/dist/components/tabs/tabs.vue.d.ts +2 -2
  33. package/dist/components/textarea/style2.css +1 -0
  34. package/dist/components/textarea/textarea.vue.d.ts +1 -1
  35. package/dist/components/tree/tree.vue.d.ts +1 -1
  36. package/dist/components/tree-select/style2.css +6 -15
  37. package/dist/components/tree-select/tree-select.js +95 -41
  38. package/dist/components/tree-select/tree-select.js.map +1 -1
  39. package/dist/components/tree-select/tree-select.vue.d.ts +1 -1
  40. package/package.json +6 -6
  41. package/src/components/cascade/cascade.vue +15 -2
  42. package/src/components/cascade/style.scss +5 -0
  43. package/src/components/date-range-picker/date-range-picker.vue +9 -1
  44. package/src/components/date-range-picker/style.scss +5 -0
  45. package/src/components/form-item/form-item.vue +2 -2
  46. package/src/components/form-item/style.scss +2 -1
  47. package/src/components/grid-input/style.scss +1 -0
  48. package/src/components/multi-select/multi-select.vue +50 -19
  49. package/src/components/multi-select/style.scss +27 -4
  50. package/src/components/multi-tree-select/multi-tree-select.vue +34 -14
  51. package/src/components/multi-tree-select/style.scss +28 -9
  52. package/src/components/select/__test__/select.test.ts +110 -0
  53. package/src/components/select/select.vue +130 -46
  54. package/src/components/select/style.scss +5 -14
  55. package/src/components/select/use-options.ts +83 -39
  56. package/src/components/textarea/style.scss +1 -0
  57. package/src/components/tree-select/style.scss +4 -8
  58. package/src/components/tree-select/tree-select.vue +90 -17
@@ -5,12 +5,11 @@ import _sfc_main$4 from "../dropdown/dropdown.js";
5
5
  import input_default from "../input/input.js";
6
6
  import { useKeyboard } from "./use-keyboard.js";
7
7
  import { useOptions } from "./use-options.js";
8
- import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createSlots, createTextVNode, createVNode, defineComponent, isRef, mergeModels, mergeProps, nextTick, normalizeClass, normalizeStyle, openBlock, renderList, renderSlot, shallowRef, toDisplayString, unref, useModel, watch, withCtx, withDirectives } from "vue";
8
+ import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createSlots, createTextVNode, createVNode, defineComponent, mergeModels, mergeProps, nextTick, normalizeClass, normalizeStyle, openBlock, renderList, renderSlot, shallowRef, toDisplayString, unref, useModel, useTemplateRef, watch, withCtx, withModifiers } from "vue";
9
9
  import { useFormFallbackProps, useVirtualizer } from "@veltra/compositions";
10
10
  import { FORM_EMPTY_CONTENT, bem, fieldKey, injectFormContext, scrollIntoContainerView, withUnit } from "@veltra/utils";
11
- import { ArrowDown, Search } from "@veltra/icons/normal";
11
+ import { ArrowDown, Close } from "@veltra/icons/normal";
12
12
  import { o } from "@cat-kit/core";
13
- import { vFocus } from "@veltra/directives";
14
13
  //#region src/components/select/select.vue
15
14
  const _hoisted_1 = ["onClick", "data-index"];
16
15
  const _hoisted_2 = [
@@ -85,21 +84,67 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
85
84
  });
86
85
  const dropdownRef = shallowRef();
87
86
  const scrollRef = shallowRef();
87
+ const inputRef = useTemplateRef("inputRef");
88
88
  const filterable = computed(() => {
89
89
  return props.filterable || typeof props.options === "function";
90
90
  });
91
91
  const { queryString, options, temOptionsToCreatedOptions, clearCreatedOptions } = useOptions({ props });
92
+ const dropdownVisible = shallowRef(false);
93
+ /**
94
+ * 是否处于查询态:决定触发输入框显示查询串还是选中标签。
95
+ * 与面板可见状态解耦 —— 选择后立即退出查询态,
96
+ * 不等面板关闭动画结束(否则显示值会延迟一个动画时长才恢复)。
97
+ */
98
+ const querying = shallowRef(false);
99
+ /** 触发输入框的值:查询态下承载查询串,否则展示选中标签 */
100
+ const inputValue = computed(() => {
101
+ if (filterable.value && querying.value) return queryString.value;
102
+ return displayedValue.value;
103
+ });
104
+ /** 查询态下已选标签降级为占位提示 */
105
+ const inputPlaceholder = computed(() => {
106
+ if (filterable.value && querying.value && displayedValue.value) return displayedValue.value;
107
+ return props.placeholder;
108
+ });
109
+ function handleQueryInput(value) {
110
+ if (!filterable.value) return;
111
+ queryString.value = value;
112
+ }
113
+ const hovered = shallowRef(false);
114
+ /** 悬停且存在选中值时展示清除按钮(替代下拉箭头) */
115
+ const showClear = computed(() => {
116
+ return props.clearable && !disabled.value && hovered.value && !!displayedValue.value;
117
+ });
118
+ /**
119
+ * 过滤模式下拦截输入区域的点击:
120
+ * 阻止 dropdown 的 trigger 开合切换,保持面板展开以不中断输入。
121
+ * 非输入区域(箭头、留白)放行,维持原开合行为。
122
+ */
123
+ function handleTriggerClickCapture(e) {
124
+ if (!filterable.value || disabled.value) return;
125
+ if (!(e.target instanceof HTMLInputElement)) return;
126
+ e.stopPropagation();
127
+ if (!dropdownVisible.value) dropdownRef.value?.open();
128
+ }
92
129
  let userSelecting = false;
93
130
  function lock() {
94
131
  userSelecting = true;
95
132
  }
96
- function unlock() {
133
+ /**
134
+ * @param preferredValue 选择时传入刚提交的值,避免 nextTick 时 props.modelValue 尚未回传而用旧值覆盖高亮/标签
135
+ */
136
+ function unlock(preferredValue) {
97
137
  nextTick(() => {
98
138
  userSelecting = false;
139
+ syncSelected(preferredValue !== void 0 ? preferredValue : props.modelValue, options.value);
99
140
  });
100
141
  }
101
- watch([() => props.modelValue, options], ([modelValue, options]) => {
102
- if (userSelecting) return;
142
+ /** 按 modelValue 与选项列表同步高亮索引、选中项与显示标签 */
143
+ function syncSelected(modelValue, options) {
144
+ if (queryString.value) {
145
+ currentIndex.value = options?.length ? 0 : -1;
146
+ return;
147
+ }
103
148
  if (!options?.length) return;
104
149
  if (modelValue !== void 0 && modelValue !== null && modelValue !== "") {
105
150
  currentIndex.value = options.findIndex((option) => o(option).get(valueKey.value) === modelValue);
@@ -110,6 +155,10 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
110
155
  selected.value = void 0;
111
156
  label.value = void 0;
112
157
  }
158
+ }
159
+ watch([() => props.modelValue, options], ([modelValue, options]) => {
160
+ if (userSelecting) return;
161
+ syncSelected(modelValue, options);
113
162
  }, { immediate: true });
114
163
  const baseVirtualEnabled = computed(() => options.value.length > 80);
115
164
  const virtualEnabled = computed(() => baseVirtualEnabled.value && !props.grid);
@@ -148,24 +197,36 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
148
197
  if (selectedEl) scrollIntoContainerView(selectedEl, scroll.containerRef ?? null);
149
198
  }
150
199
  });
151
- const dropdownVisible = shallowRef(false);
152
- watch(dropdownVisible, (v) => {
153
- if (!v) queryString.value = "";
200
+ watch(dropdownVisible, (visible) => {
201
+ if (visible) {
202
+ if (filterable.value) {
203
+ queryString.value = "";
204
+ querying.value = true;
205
+ nextTick(() => inputRef.value?.el?.focus());
206
+ }
207
+ } else {
208
+ querying.value = false;
209
+ queryString.value = "";
210
+ }
154
211
  });
155
212
  const handleDropdownVisible = (visible) => {
156
213
  if (!visible) queryString.value = "";
157
214
  };
158
215
  /** 单选 */
159
- const handleSelect = (option, index) => {
216
+ const handleSelect = (option, _index) => {
160
217
  lock();
218
+ const value = option?.[valueKey.value];
161
219
  selected.value = option;
162
- emit("update:modelValue", option?.[valueKey.value]);
220
+ emit("update:modelValue", value);
163
221
  label.value = option?.[labelKey.value];
164
222
  emit("change", option);
165
223
  if (option.__isTemp) temOptionsToCreatedOptions();
166
- currentIndex.value = index;
224
+ querying.value = false;
225
+ queryString.value = "";
226
+ currentIndex.value = options.value.findIndex((item) => o(item).get(valueKey.value) === value);
227
+ if (currentIndex.value >= 0) selected.value = options.value[currentIndex.value];
167
228
  dropdownRef.value?.close();
168
- unlock();
229
+ unlock(value);
169
230
  };
170
231
  /** 清除选项 */
171
232
  const handleClear = () => {
@@ -176,7 +237,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
176
237
  clearCreatedOptions();
177
238
  emit("update:modelValue", void 0);
178
239
  emit("change", void 0);
179
- unlock();
240
+ unlock(null);
180
241
  };
181
242
  function getCurrentEl() {
182
243
  return scrollRef.value?.contentRef?.querySelector("li.is-selected");
@@ -194,7 +255,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
194
255
  ref_key: "dropdownRef",
195
256
  ref: dropdownRef,
196
257
  visible: dropdownVisible.value,
197
- "onUpdate:visible": [_cache[1] || (_cache[1] = ($event) => dropdownVisible.value = $event), handleDropdownVisible],
258
+ "onUpdate:visible": [_cache[0] || (_cache[0] = ($event) => dropdownVisible.value = $event), handleDropdownVisible],
198
259
  "content-class": [
199
260
  unref(cls).e("panel"),
200
261
  unref(cls).em("panel", unref(size)),
@@ -204,22 +265,44 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
204
265
  disabled: unref(disabled),
205
266
  "min-width": __props.minWidth,
206
267
  width: __props.width,
207
- onKeydown: unref(handleKeydown)
268
+ onKeydown: unref(handleKeydown),
269
+ onMouseenter: _cache[1] || (_cache[1] = ($event) => hovered.value = true),
270
+ onMouseleave: _cache[2] || (_cache[2] = ($event) => hovered.value = false)
208
271
  }), {
209
272
  trigger: withCtx(() => [createVNode(unref(input_default), {
273
+ ref_key: "inputRef",
274
+ ref: inputRef,
210
275
  size: unref(size),
211
276
  disabled: unref(disabled),
212
- placeholder: __props.placeholder,
213
- clearable: __props.clearable,
214
- "model-value": displayedValue.value,
215
- onClear: handleClear,
277
+ placeholder: inputPlaceholder.value,
278
+ clearable: false,
279
+ "model-value": inputValue.value,
280
+ "onUpdate:modelValue": handleQueryInput,
216
281
  onKeydown: unref(handleKeydown),
217
- "native-readonly": ""
282
+ "native-readonly": !filterable.value || !querying.value,
283
+ onClickCapture: handleTriggerClickCapture
218
284
  }, createSlots({
219
- suffix: withCtx(() => [createVNode(unref(_sfc_main$1), { class: normalizeClass(unref(cls).e("arrow")) }, {
220
- default: withCtx(() => [createVNode(unref(ArrowDown))]),
285
+ suffix: withCtx(() => [createVNode(Transition, {
286
+ name: "zoom-in",
287
+ mode: "out-in"
288
+ }, {
289
+ default: withCtx(() => [showClear.value ? (openBlock(), createBlock(unref(_sfc_main$1), {
290
+ class: normalizeClass(unref(cls).e("clear")),
291
+ title: "清除",
292
+ key: "clear",
293
+ onClick: withModifiers(handleClear, ["stop"])
294
+ }, {
295
+ default: withCtx(() => [createVNode(unref(Close))]),
296
+ _: 1
297
+ }, 8, ["class"])) : (openBlock(), createBlock(unref(_sfc_main$1), {
298
+ class: normalizeClass(unref(cls).e("arrow")),
299
+ key: "arrow"
300
+ }, {
301
+ default: withCtx(() => [createVNode(unref(ArrowDown))]),
302
+ _: 1
303
+ }, 8, ["class"]))]),
221
304
  _: 1
222
- }, 8, ["class"])]),
305
+ })]),
223
306
  _: 2
224
307
  }, [_ctx.$slots.prefix ? {
225
308
  name: "prefix",
@@ -229,79 +312,60 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
229
312
  "size",
230
313
  "disabled",
231
314
  "placeholder",
232
- "clearable",
233
315
  "model-value",
234
- "onKeydown"
316
+ "onKeydown",
317
+ "native-readonly"
235
318
  ])]),
236
- content: withCtx(() => [
237
- createCommentVNode(" 过滤器 "),
238
- filterable.value ? (openBlock(), createElementBlock("div", {
239
- key: 0,
240
- class: normalizeClass(unref(cls).e("content-filter"))
241
- }, [withDirectives((openBlock(), createBlock(unref(input_default), {
242
- placeholder: "输入关键字进行搜索",
243
- tabindex: "0",
244
- modelValue: unref(queryString),
245
- "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isRef(queryString) ? queryString.value = $event : null)
246
- }, {
247
- suffix: withCtx(() => [createVNode(unref(_sfc_main$1), null, {
248
- default: withCtx(() => [createVNode(unref(Search))]),
249
- _: 1
250
- })]),
251
- _: 1
252
- }, 8, ["modelValue"])), [[unref(vFocus)]])], 2)) : createCommentVNode("v-if", true),
253
- createCommentVNode("\n 单选列表。虚拟化启用时,内容容器 height 由 useVirtualizer 命令式写入;\n 此处 content-style 仅承担 grid 相关样式,避免 height 变化触发模板重渲染。\n "),
254
- unref(options).length ? (openBlock(), createBlock(unref(_sfc_main$3), {
255
- key: 1,
256
- tag: "ul",
257
- class: normalizeClass(unref(cls).e("options")),
258
- ref_key: "scrollRef",
259
- ref: scrollRef,
260
- "content-class": [
261
- unref(cls).e("options-wrap"),
262
- unref(bem).is("virtual", virtualEnabled.value),
263
- unref(bem).is("grid", !!__props.grid)
264
- ],
265
- "content-style": {
266
- gridTemplateColumns: __props.grid ? `repeat(${__props.grid.cols}, minmax(0px, 1fr))` : void 0,
267
- gridGap: __props.grid ? unref(withUnit)(__props.grid.gap, "px") : void 0
268
- }
269
- }, {
270
- default: withCtx(() => [virtualEnabled.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createCommentVNode(" @vue-ignore "), (openBlock(true), createElementBlock(Fragment, null, renderList(virtualOptions.value, ({ option, index, val, label, key, offset }) => {
271
- return openBlock(), createElementBlock("li", {
272
- class: normalizeClass([unref(optionClass), unref(bem).is("selected", index === currentIndex.value)]),
273
- onClick: ($event) => handleSelect(option, index),
274
- key,
275
- style: normalizeStyle({ transform: `translateY(${offset}px)` }),
276
- "data-index": index,
277
- ref_for: true,
278
- ref: (el) => measureElement(index, el)
279
- }, [renderSlot(_ctx.$slots, "default", mergeProps({ ref_for: true }, {
280
- option,
281
- index
282
- }), () => [createTextVNode(toDisplayString(label), 1)])], 14, _hoisted_1);
283
- }), 128))], 64)) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(unref(options), (option, index) => {
284
- return openBlock(), createElementBlock("li", {
285
- class: normalizeClass([unref(optionClass), unref(bem).is("selected", index === currentIndex.value)]),
286
- onClick: ($event) => handleSelect(option, index),
287
- "data-index": index,
288
- title: unref(o)(option).get(labelKey.value),
289
- key: unref(o)(option).get(valueKey.value)
290
- }, [renderSlot(_ctx.$slots, "default", mergeProps({ ref_for: true }, {
291
- option,
292
- index
293
- }), () => [createTextVNode(toDisplayString(unref(o)(option).get(labelKey.value)), 1)])], 10, _hoisted_2);
294
- }), 128))]),
295
- _: 3
296
- }, 8, [
297
- "class",
298
- "content-class",
299
- "content-style"
300
- ])) : (openBlock(), createElementBlock("div", {
301
- key: 2,
302
- class: normalizeClass(unref(cls).e("empty"))
303
- }, [createVNode(unref(_sfc_main$2))], 2))
304
- ]),
319
+ content: withCtx(() => [createCommentVNode("\n 单选列表。虚拟化启用时,内容容器 height 由 useVirtualizer 命令式写入;\n 此处 content-style 仅承担 grid 相关样式,避免 height 变化触发模板重渲染。\n "), unref(options).length ? (openBlock(), createBlock(unref(_sfc_main$3), {
320
+ key: 0,
321
+ tag: "ul",
322
+ class: normalizeClass(unref(cls).e("options")),
323
+ ref_key: "scrollRef",
324
+ ref: scrollRef,
325
+ "content-class": [
326
+ unref(cls).e("options-wrap"),
327
+ unref(bem).is("virtual", virtualEnabled.value),
328
+ unref(bem).is("grid", !!__props.grid)
329
+ ],
330
+ "content-style": {
331
+ gridTemplateColumns: __props.grid ? `repeat(${__props.grid.cols}, minmax(0px, 1fr))` : void 0,
332
+ gridGap: __props.grid ? unref(withUnit)(__props.grid.gap, "px") : void 0
333
+ }
334
+ }, {
335
+ default: withCtx(() => [virtualEnabled.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createCommentVNode(" @vue-ignore "), (openBlock(true), createElementBlock(Fragment, null, renderList(virtualOptions.value, ({ option, index, val, label, key, offset }) => {
336
+ return openBlock(), createElementBlock("li", {
337
+ class: normalizeClass([unref(optionClass), unref(bem).is("selected", index === currentIndex.value)]),
338
+ onClick: ($event) => handleSelect(option, index),
339
+ key,
340
+ style: normalizeStyle({ transform: `translateY(${offset}px)` }),
341
+ "data-index": index,
342
+ ref_for: true,
343
+ ref: (el) => measureElement(index, el)
344
+ }, [renderSlot(_ctx.$slots, "default", mergeProps({ ref_for: true }, {
345
+ option,
346
+ index
347
+ }), () => [createTextVNode(toDisplayString(label), 1)])], 14, _hoisted_1);
348
+ }), 128))], 64)) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(unref(options), (option, index) => {
349
+ return openBlock(), createElementBlock("li", {
350
+ class: normalizeClass([unref(optionClass), unref(bem).is("selected", index === currentIndex.value)]),
351
+ onClick: ($event) => handleSelect(option, index),
352
+ "data-index": index,
353
+ title: unref(o)(option).get(labelKey.value),
354
+ key: unref(o)(option).get(valueKey.value)
355
+ }, [renderSlot(_ctx.$slots, "default", mergeProps({ ref_for: true }, {
356
+ option,
357
+ index
358
+ }), () => [createTextVNode(toDisplayString(unref(o)(option).get(labelKey.value)), 1)])], 10, _hoisted_2);
359
+ }), 128))]),
360
+ _: 3
361
+ }, 8, [
362
+ "class",
363
+ "content-class",
364
+ "content-style"
365
+ ])) : (openBlock(), createElementBlock("div", {
366
+ key: 1,
367
+ class: normalizeClass(unref(cls).e("empty"))
368
+ }, [createVNode(unref(_sfc_main$2))], 2))]),
305
369
  _: 3
306
370
  }, 16, [
307
371
  "class",
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","names":["$attrs","$slots"],"sources":["../../../src/components/select/select.vue"],"sourcesContent":["<template>\n <u-dropdown\n v-if=\"!readonly\"\n v-bind=\"$attrs\"\n trigger=\"click\"\n :class=\"[cls.b, bem.is('disabled', disabled)]\"\n ref=\"dropdownRef\"\n v-model:visible=\"dropdownVisible\"\n :content-class=\"[cls.e('panel'), cls.em('panel', size), contentClass]\"\n :content-style=\"contentStyle\"\n :disabled=\"disabled\"\n :min-width=\"minWidth\"\n :width=\"width\"\n @keydown=\"handleKeydown\"\n @update:visible=\"handleDropdownVisible\"\n >\n <!-- 触发 -->\n <template #trigger>\n <u-input\n :size=\"size\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :clearable=\"clearable\"\n :model-value=\"displayedValue\"\n @clear=\"handleClear\"\n @keydown=\"handleKeydown\"\n native-readonly\n >\n <template #prefix v-if=\"$slots.prefix\">\n <slot name=\"prefix\" />\n </template>\n\n <template #suffix>\n <u-icon :class=\"cls.e('arrow')\">\n <ArrowDown />\n </u-icon>\n </template>\n </u-input>\n </template>\n\n <!-- 下拉内容 -->\n <template #content>\n <!-- 过滤器 -->\n <div v-if=\"filterable\" :class=\"cls.e('content-filter')\">\n <u-input placeholder=\"输入关键字进行搜索\" tabindex=\"0\" v-focus v-model=\"queryString\">\n <template #suffix>\n <u-icon>\n <Search />\n </u-icon>\n </template>\n </u-input>\n </div>\n\n <!--\n 单选列表。虚拟化启用时,内容容器 height 由 useVirtualizer 命令式写入;\n 此处 content-style 仅承担 grid 相关样式,避免 height 变化触发模板重渲染。\n -->\n <u-scroll\n v-if=\"options.length\"\n tag=\"ul\"\n :class=\"cls.e('options')\"\n ref=\"scrollRef\"\n :content-class=\"[\n cls.e('options-wrap'),\n bem.is('virtual', virtualEnabled),\n bem.is('grid', !!grid)\n ]\"\n :content-style=\"{\n gridTemplateColumns: grid ? `repeat(${grid.cols}, minmax(0px, 1fr))` : undefined,\n gridGap: grid ? withUnit(grid.gap, 'px') : undefined\n }\"\n >\n <template v-if=\"virtualEnabled\">\n <!-- @vue-ignore -->\n <li\n v-for=\"{ option, index, val, label, key, offset } of virtualOptions\"\n :class=\"[optionClass, bem.is('selected', index === currentIndex)]\"\n @click=\"handleSelect(option, index)\"\n :key=\"key\"\n :style=\"{ transform: `translateY(${offset}px)` }\"\n :data-index=\"index\"\n :ref=\"(el) => measureElement(index, el as Element | null)\"\n >\n <slot v-bind=\"{ option, index }\">\n {{ label }}\n </slot>\n </li>\n </template>\n\n <template v-else>\n <li\n v-for=\"(option, index) of options\"\n :class=\"[optionClass, bem.is('selected', index === currentIndex)]\"\n @click=\"handleSelect(option, index)\"\n :data-index=\"index\"\n :title=\"o(option).get(labelKey)\"\n :key=\"o(option).get(valueKey)\"\n >\n <slot v-bind=\"{ option, index }\">\n {{ o(option).get(labelKey) }}\n </slot>\n </li>\n </template>\n </u-scroll>\n\n <div v-else :class=\"cls.e('empty')\">\n <UEmpty />\n </div>\n </template>\n </u-dropdown>\n\n <template v-else>\n {{ displayedValue || FORM_EMPTY_CONTENT }}\n </template>\n</template>\n\n<script lang=\"ts\" setup>\nimport { o } from '@cat-kit/core'\nimport { useFormFallbackProps, useVirtualizer } from '@veltra/compositions'\nimport { vFocus } from '@veltra/directives'\nimport { ArrowDown, Search } from '@veltra/icons/normal'\nimport { bem, fieldKey, FORM_EMPTY_CONTENT, scrollIntoContainerView, withUnit } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { computed, nextTick, shallowRef, watch } from 'vue'\n\nimport type {\n SelectEmits,\n SelectProps,\n _SelectExposed,\n DropdownExposed,\n ScrollExposed\n} from '../../types'\nimport { UDropdown } from '../dropdown'\nimport { UEmpty } from '../empty'\nimport { UIcon } from '../icon'\nimport { UInput } from '../input'\nimport { UScroll } from '../scroll'\nimport { useKeyboard } from './use-keyboard'\nimport { useOptions } from './use-options'\n\ndefineOptions({ name: 'USelect', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SelectProps>(), {\n labelKey: 'label',\n valueKey: 'value',\n placeholder: '请选择',\n clearable: true,\n disabled: undefined,\n readonly: undefined\n})\n\nconst emit = defineEmits<SelectEmits>()\n\ndefineSlots<{\n /** 前缀插槽 */\n prefix?: () => any\n /** 默认插槽 */\n default?: (scope: { option: Record<string, any>; index: number }) => any\n}>()\n\nconst cls = bem('select')\n\nconst labelKey = computed(() => fieldKey(props.labelKey, 'label'))\nconst valueKey = computed(() => fieldKey(props.valueKey, 'value'))\n\nconst optionClass = cls.e('option')\n\nconst { formProps } = injectFormContext()\nconst { size, disabled, readonly } = useFormFallbackProps([formProps ?? {}, props], {\n size: 'default',\n disabled: false,\n readonly: false\n})\n\nconst currentIndex = shallowRef(-1)\nconst label = defineModel('text')\nconst selected = shallowRef<Record<string, any>>()\n\nconst displayedValue = computed(() => {\n if (label.value) return label.value\n\n return selected.value ? o(selected.value).get(labelKey.value) : String(props.modelValue ?? '')\n})\n\nconst dropdownRef = shallowRef<DropdownExposed>()\nconst scrollRef = shallowRef<ScrollExposed>()\n\nconst filterable = computed(() => {\n return props.filterable || typeof props.options === 'function'\n})\n\nconst { queryString, options, temOptionsToCreatedOptions, clearCreatedOptions } = useOptions({\n props\n})\n\n// TODO: 优化\nlet userSelecting = false\nfunction lock() {\n userSelecting = true\n}\n\nfunction unlock() {\n nextTick(() => {\n userSelecting = false\n })\n}\n\n// 回显\nwatch(\n [() => props.modelValue, options],\n ([modelValue, options]) => {\n if (userSelecting) return\n\n if (!options?.length) return\n\n if (modelValue !== undefined && modelValue !== null && modelValue !== '') {\n currentIndex.value = options.findIndex(\n (option) => o(option).get(valueKey.value) === modelValue\n )\n selected.value = currentIndex.value >= 0 ? options[currentIndex.value] : undefined\n label.value = selected.value ? o(selected.value).get(labelKey.value) : undefined\n } else {\n currentIndex.value = -1\n selected.value = undefined\n label.value = undefined\n }\n },\n { immediate: true }\n)\n\nconst baseVirtualEnabled = computed(() => options.value.length > 80)\nconst virtualEnabled = computed(() => baseVirtualEnabled.value && !props.grid)\n\nconst { virtualizer, items } = useVirtualizer({\n count: computed(() => options.value.length),\n scrollEl: () => scrollRef.value?.containerRef ?? null,\n contentEl: () => (virtualEnabled.value ? (scrollRef.value?.contentRef ?? null) : null),\n gap: 4,\n estimateSize: () => 40\n})\n\nconst virtualOptions = computed(() => {\n const _options = options.value\n return items.value.map((item) => {\n const option = _options[item.index]!\n return {\n option,\n index: item.index,\n label: o(option).get(labelKey.value),\n val: o(option).get(valueKey.value),\n key: item.index,\n offset: item.start\n }\n })\n})\n\nconst measureElement: (index: number, el: Element | null) => void = (index, el) =>\n virtualizer.measureElement(index, el)\n\nfunction scrollTo(index: number): void {\n virtualizer.scrollToIndex(index, { align: 'center' })\n}\n\nwatch([scrollRef, virtualEnabled], ([scroll, virtualEnabled]) => {\n if (!scroll || !props.modelValue) return\n\n if (virtualEnabled) {\n const index = options.value.findIndex((option) => option === selected.value)\n index !== -1 && dropdownVisible.value && nextTick(() => scrollTo(index))\n } else {\n const selectedEl = scroll?.contentRef?.getElementsByClassName('is-selected')[0]\n if (selectedEl) {\n scrollIntoContainerView(selectedEl as HTMLElement, scroll.containerRef ?? null)\n }\n }\n})\n\nconst dropdownVisible = shallowRef(false)\n\nwatch(dropdownVisible, (v) => {\n if (!v) {\n queryString.value = ''\n }\n})\n\nconst handleDropdownVisible = (visible: boolean) => {\n if (!visible) {\n queryString.value = ''\n }\n}\n\n/** 单选 */\nconst handleSelect = (option: Record<string, any>, index: number) => {\n lock()\n selected.value = option\n\n emit('update:modelValue', option?.[valueKey.value])\n label.value = option?.[labelKey.value]\n emit('change', option)\n if (option.__isTemp) {\n temOptionsToCreatedOptions()\n }\n currentIndex.value = index\n dropdownRef.value?.close()\n\n unlock()\n}\n\n/** 清除选项 */\nconst handleClear = () => {\n lock()\n selected.value = undefined\n currentIndex.value = -1\n label.value = undefined\n clearCreatedOptions()\n emit('update:modelValue', undefined)\n emit('change', undefined)\n unlock()\n}\n\nfunction getCurrentEl() {\n return scrollRef.value?.contentRef?.querySelector('li.is-selected') as HTMLElement | undefined\n}\n\nconst { handleKeydown } = useKeyboard({\n options,\n currentIndex,\n selectOption: handleSelect,\n getCurrentEl\n})\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"select.js","names":["$attrs","$slots"],"sources":["../../../src/components/select/select.vue"],"sourcesContent":["<template>\n <u-dropdown\n v-if=\"!readonly\"\n v-bind=\"$attrs\"\n trigger=\"click\"\n :class=\"[cls.b, bem.is('disabled', disabled)]\"\n ref=\"dropdownRef\"\n v-model:visible=\"dropdownVisible\"\n :content-class=\"[cls.e('panel'), cls.em('panel', size), contentClass]\"\n :content-style=\"contentStyle\"\n :disabled=\"disabled\"\n :min-width=\"minWidth\"\n :width=\"width\"\n @keydown=\"handleKeydown\"\n @update:visible=\"handleDropdownVisible\"\n @mouseenter=\"hovered = true\"\n @mouseleave=\"hovered = false\"\n >\n <!-- 触发 -->\n <template #trigger>\n <u-input\n ref=\"inputRef\"\n :size=\"size\"\n :disabled=\"disabled\"\n :placeholder=\"inputPlaceholder\"\n :clearable=\"false\"\n :model-value=\"inputValue\"\n @update:model-value=\"handleQueryInput\"\n @keydown=\"handleKeydown\"\n :native-readonly=\"!filterable || !querying\"\n @click.capture=\"handleTriggerClickCapture\"\n >\n <template #prefix v-if=\"$slots.prefix\">\n <slot name=\"prefix\" />\n </template>\n\n <template #suffix>\n <transition name=\"zoom-in\" mode=\"out-in\">\n <u-icon\n v-if=\"showClear\"\n :class=\"cls.e('clear')\"\n title=\"清除\"\n key=\"clear\"\n @click.stop=\"handleClear\"\n >\n <Close />\n </u-icon>\n <u-icon v-else :class=\"cls.e('arrow')\" key=\"arrow\">\n <ArrowDown />\n </u-icon>\n </transition>\n </template>\n </u-input>\n </template>\n\n <!-- 下拉内容 -->\n <template #content>\n <!--\n 单选列表。虚拟化启用时,内容容器 height 由 useVirtualizer 命令式写入;\n 此处 content-style 仅承担 grid 相关样式,避免 height 变化触发模板重渲染。\n -->\n <u-scroll\n v-if=\"options.length\"\n tag=\"ul\"\n :class=\"cls.e('options')\"\n ref=\"scrollRef\"\n :content-class=\"[\n cls.e('options-wrap'),\n bem.is('virtual', virtualEnabled),\n bem.is('grid', !!grid)\n ]\"\n :content-style=\"{\n gridTemplateColumns: grid ? `repeat(${grid.cols}, minmax(0px, 1fr))` : undefined,\n gridGap: grid ? withUnit(grid.gap, 'px') : undefined\n }\"\n >\n <template v-if=\"virtualEnabled\">\n <!-- @vue-ignore -->\n <li\n v-for=\"{ option, index, val, label, key, offset } of virtualOptions\"\n :class=\"[optionClass, bem.is('selected', index === currentIndex)]\"\n @click=\"handleSelect(option, index)\"\n :key=\"key\"\n :style=\"{ transform: `translateY(${offset}px)` }\"\n :data-index=\"index\"\n :ref=\"(el) => measureElement(index, el as Element | null)\"\n >\n <slot v-bind=\"{ option, index }\">\n {{ label }}\n </slot>\n </li>\n </template>\n\n <template v-else>\n <li\n v-for=\"(option, index) of options\"\n :class=\"[optionClass, bem.is('selected', index === currentIndex)]\"\n @click=\"handleSelect(option, index)\"\n :data-index=\"index\"\n :title=\"o(option).get(labelKey)\"\n :key=\"o(option).get(valueKey)\"\n >\n <slot v-bind=\"{ option, index }\">\n {{ o(option).get(labelKey) }}\n </slot>\n </li>\n </template>\n </u-scroll>\n\n <div v-else :class=\"cls.e('empty')\">\n <UEmpty />\n </div>\n </template>\n </u-dropdown>\n\n <template v-else>\n {{ displayedValue || FORM_EMPTY_CONTENT }}\n </template>\n</template>\n\n<script lang=\"ts\" setup>\nimport { o } from '@cat-kit/core'\nimport { useFormFallbackProps, useVirtualizer } from '@veltra/compositions'\nimport { ArrowDown, Close } from '@veltra/icons/normal'\nimport { bem, fieldKey, FORM_EMPTY_CONTENT, scrollIntoContainerView, withUnit } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { computed, nextTick, shallowRef, useTemplateRef, watch } from 'vue'\n\nimport type {\n SelectEmits,\n SelectProps,\n _SelectExposed,\n DropdownExposed,\n InputExposed,\n ScrollExposed\n} from '../../types'\nimport { UDropdown } from '../dropdown'\nimport { UEmpty } from '../empty'\nimport { UIcon } from '../icon'\nimport { UInput } from '../input'\nimport { UScroll } from '../scroll'\nimport { useKeyboard } from './use-keyboard'\nimport { useOptions } from './use-options'\n\ndefineOptions({ name: 'USelect', inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SelectProps>(), {\n labelKey: 'label',\n valueKey: 'value',\n placeholder: '请选择',\n clearable: true,\n disabled: undefined,\n readonly: undefined\n})\n\nconst emit = defineEmits<SelectEmits>()\n\ndefineSlots<{\n /** 前缀插槽 */\n prefix?: () => any\n /** 默认插槽 */\n default?: (scope: { option: Record<string, any>; index: number }) => any\n}>()\n\nconst cls = bem('select')\n\nconst labelKey = computed(() => fieldKey(props.labelKey, 'label'))\nconst valueKey = computed(() => fieldKey(props.valueKey, 'value'))\n\nconst optionClass = cls.e('option')\n\nconst { formProps } = injectFormContext()\nconst { size, disabled, readonly } = useFormFallbackProps([formProps ?? {}, props], {\n size: 'default',\n disabled: false,\n readonly: false\n})\n\nconst currentIndex = shallowRef(-1)\nconst label = defineModel('text')\nconst selected = shallowRef<Record<string, any>>()\n\nconst displayedValue = computed(() => {\n if (label.value) return label.value\n\n return selected.value ? o(selected.value).get(labelKey.value) : String(props.modelValue ?? '')\n})\n\nconst dropdownRef = shallowRef<DropdownExposed>()\nconst scrollRef = shallowRef<ScrollExposed>()\nconst inputRef = useTemplateRef<InputExposed>('inputRef')\n\nconst filterable = computed(() => {\n return props.filterable || typeof props.options === 'function'\n})\n\nconst { queryString, options, temOptionsToCreatedOptions, clearCreatedOptions } = useOptions({\n props\n})\n\nconst dropdownVisible = shallowRef(false)\n\n/**\n * 是否处于查询态:决定触发输入框显示查询串还是选中标签。\n * 与面板可见状态解耦 —— 选择后立即退出查询态,\n * 不等面板关闭动画结束(否则显示值会延迟一个动画时长才恢复)。\n */\nconst querying = shallowRef(false)\n\n/** 触发输入框的值:查询态下承载查询串,否则展示选中标签 */\nconst inputValue = computed(() => {\n if (filterable.value && querying.value) return queryString.value\n return displayedValue.value\n})\n\n/** 查询态下已选标签降级为占位提示 */\nconst inputPlaceholder = computed(() => {\n if (filterable.value && querying.value && displayedValue.value) {\n return displayedValue.value\n }\n return props.placeholder\n})\n\nfunction handleQueryInput(value: string) {\n if (!filterable.value) return\n queryString.value = value\n}\n\nconst hovered = shallowRef(false)\n\n/** 悬停且存在选中值时展示清除按钮(替代下拉箭头) */\nconst showClear = computed(() => {\n return props.clearable && !disabled.value && hovered.value && !!displayedValue.value\n})\n\n/**\n * 过滤模式下拦截输入区域的点击:\n * 阻止 dropdown 的 trigger 开合切换,保持面板展开以不中断输入。\n * 非输入区域(箭头、留白)放行,维持原开合行为。\n */\nfunction handleTriggerClickCapture(e: MouseEvent) {\n if (!filterable.value || disabled.value) return\n if (!(e.target instanceof HTMLInputElement)) return\n\n e.stopPropagation()\n if (!dropdownVisible.value) dropdownRef.value?.open()\n}\n\n// TODO: 优化\nlet userSelecting = false\nfunction lock() {\n userSelecting = true\n}\n\n/**\n * @param preferredValue 选择时传入刚提交的值,避免 nextTick 时 props.modelValue 尚未回传而用旧值覆盖高亮/标签\n */\nfunction unlock(preferredValue?: any) {\n nextTick(() => {\n userSelecting = false\n // 选择/清除会同步重建选项列表(临时项转正、查询串清空),\n // 重建触发的回显在锁定期间被跳过,解锁后按最新列表重同步一次,\n // 避免高亮停留在选择前的旧索引上\n const value = preferredValue !== undefined ? preferredValue : props.modelValue\n syncSelected(value, options.value)\n })\n}\n\n/** 按 modelValue 与选项列表同步高亮索引、选中项与显示标签 */\nfunction syncSelected(modelValue: any, options: Record<string, any>[] | undefined) {\n // 查询输入期间高亮第一项(创建模式下通常是临时项,便于回车创建)\n if (queryString.value) {\n currentIndex.value = options?.length ? 0 : -1\n return\n }\n\n if (!options?.length) return\n\n if (modelValue !== undefined && modelValue !== null && modelValue !== '') {\n currentIndex.value = options.findIndex((option) => o(option).get(valueKey.value) === modelValue)\n selected.value = currentIndex.value >= 0 ? options[currentIndex.value] : undefined\n label.value = selected.value ? o(selected.value).get(labelKey.value) : undefined\n } else {\n currentIndex.value = -1\n selected.value = undefined\n label.value = undefined\n }\n}\n\n// 回显\nwatch(\n [() => props.modelValue, options],\n ([modelValue, options]) => {\n if (userSelecting) return\n syncSelected(modelValue, options)\n },\n { immediate: true }\n)\n\nconst baseVirtualEnabled = computed(() => options.value.length > 80)\nconst virtualEnabled = computed(() => baseVirtualEnabled.value && !props.grid)\n\nconst { virtualizer, items } = useVirtualizer({\n count: computed(() => options.value.length),\n scrollEl: () => scrollRef.value?.containerRef ?? null,\n contentEl: () => (virtualEnabled.value ? (scrollRef.value?.contentRef ?? null) : null),\n gap: 4,\n estimateSize: () => 40\n})\n\nconst virtualOptions = computed(() => {\n const _options = options.value\n return items.value.map((item) => {\n const option = _options[item.index]!\n return {\n option,\n index: item.index,\n label: o(option).get(labelKey.value),\n val: o(option).get(valueKey.value),\n key: item.index,\n offset: item.start\n }\n })\n})\n\nconst measureElement: (index: number, el: Element | null) => void = (index, el) =>\n virtualizer.measureElement(index, el)\n\nfunction scrollTo(index: number): void {\n virtualizer.scrollToIndex(index, { align: 'center' })\n}\n\nwatch([scrollRef, virtualEnabled], ([scroll, virtualEnabled]) => {\n if (!scroll || !props.modelValue) return\n\n if (virtualEnabled) {\n const index = options.value.findIndex((option) => option === selected.value)\n index !== -1 && dropdownVisible.value && nextTick(() => scrollTo(index))\n } else {\n const selectedEl = scroll?.contentRef?.getElementsByClassName('is-selected')[0]\n if (selectedEl) {\n scrollIntoContainerView(selectedEl as HTMLElement, scroll.containerRef ?? null)\n }\n }\n})\n\nwatch(dropdownVisible, (visible) => {\n if (visible) {\n // 面板展开后进入查询态并聚焦输入框,可以立即输入查询\n if (filterable.value) {\n queryString.value = ''\n querying.value = true\n nextTick(() => inputRef.value?.el?.focus())\n }\n } else {\n querying.value = false\n queryString.value = ''\n }\n})\n\nconst handleDropdownVisible = (visible: boolean) => {\n if (!visible) {\n queryString.value = ''\n }\n}\n\n/** 单选 */\nconst handleSelect = (option: Record<string, any>, _index: number) => {\n lock()\n const value = option?.[valueKey.value]\n selected.value = option\n\n emit('update:modelValue', value)\n label.value = option?.[labelKey.value]\n emit('change', option)\n if (option.__isTemp) {\n // 转正后列表同步重建为完整源(临时项置顶索引失效),必须按 value 重定位\n temOptionsToCreatedOptions()\n }\n // 立即退出查询态,输入框同步恢复显示选中标签\n querying.value = false\n queryString.value = ''\n // 按当前列表定位高亮,避免创建项转正后仍停留在点击时的临时索引(通常为 0)\n currentIndex.value = options.value.findIndex((item) => o(item).get(valueKey.value) === value)\n if (currentIndex.value >= 0) {\n selected.value = options.value[currentIndex.value]\n }\n dropdownRef.value?.close()\n\n unlock(value)\n}\n\n/** 清除选项 */\nconst handleClear = () => {\n lock()\n selected.value = undefined\n currentIndex.value = -1\n label.value = undefined\n clearCreatedOptions()\n emit('update:modelValue', undefined)\n emit('change', undefined)\n unlock(null)\n}\n\nfunction getCurrentEl() {\n return scrollRef.value?.contentRef?.querySelector('li.is-selected') as HTMLElement | undefined\n}\n\nconst { handleKeydown } = useKeyboard({\n options,\n currentIndex,\n selectOption: handleSelect,\n getCurrentEl\n})\n</script>\n"],"mappings":"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"}
@@ -18,19 +18,15 @@
18
18
  .u-select {
19
19
  width: 100%;
20
20
  }
21
- .u-select__content-filter {
22
- border-bottom: 1px solid var(--u-border-color);
23
- background-color: var(--u-bg-color-middle);
24
- }
25
- .u-select__content-filter .u-input {
26
- box-shadow: none;
27
- }
28
21
  .u-select__options {
29
- max-height: 180px;
22
+ max-height: 240px;
30
23
  }
31
24
  .u-select.is-disabled .u-select__arrow {
32
25
  color: var(--u-text-color-disabled);
33
26
  }
27
+ .u-select__clear {
28
+ cursor: pointer;
29
+ }
34
30
  .u-select__options-wrap {
35
31
  list-style: none;
36
32
  margin: 0;
@@ -92,9 +88,6 @@
92
88
  .u-select__empty {
93
89
  color: var(--u-text-color-main);
94
90
  }
95
- .u-select__panel--small .u-select__content-filter {
96
- padding: calc(var(--u-gap-small) / 2) var(--u-gap-small);
97
- }
98
91
  .u-select__panel--small .u-select__options {
99
92
  padding: var(--u-gap-small);
100
93
  }
@@ -104,9 +97,6 @@
104
97
  height: var(--u-form-component-height-small);
105
98
  line-height: var(--u-form-component-height-small);
106
99
  }
107
- .u-select__panel--default .u-select__content-filter {
108
- padding: calc(var(--u-gap-default) / 2) var(--u-gap-default);
109
- }
110
100
  .u-select__panel--default .u-select__options {
111
101
  padding: var(--u-gap-default);
112
102
  }
@@ -116,9 +106,6 @@
116
106
  height: var(--u-form-component-height-default);
117
107
  line-height: var(--u-form-component-height-default);
118
108
  }
119
- .u-select__panel--large .u-select__content-filter {
120
- padding: calc(var(--u-gap-large) / 2) var(--u-gap-large);
121
- }
122
109
  .u-select__panel--large .u-select__options {
123
110
  padding: var(--u-gap-large);
124
111
  }
@@ -10,16 +10,33 @@ function useOptions(o$1) {
10
10
  const filteredOptions = shallowRef([]);
11
11
  const tempOptions = shallowRef([]);
12
12
  const createdOptions = shallowRef([]);
13
+ function getSourceOptions(propsOptions) {
14
+ if (typeof propsOptions === "function") return remoteOptions.value;
15
+ return [...createdOptions.value, ...propsOptions ?? []];
16
+ }
17
+ /** 无查询时同步展示完整列表(含已创建项),避免选择后等高亮索引错位 */
18
+ function resetToSourceOptions() {
19
+ const sourceOptions = getSourceOptions(props.options);
20
+ if (typeof props.options === "function") {
21
+ setTempOption("", remoteOptions.value);
22
+ return;
23
+ }
24
+ filteredOptions.value = sourceOptions;
25
+ tempOptions.value = [];
26
+ }
13
27
  function temOptionsToCreatedOptions() {
14
- createdOptions.value = tempOptions.value.map((item) => {
28
+ if (!tempOptions.value.length) return;
29
+ createdOptions.value = [...createdOptions.value, ...tempOptions.value.map((item) => {
15
30
  return {
16
31
  ...item,
17
32
  __isTemp: false
18
33
  };
19
- });
34
+ })];
35
+ resetToSourceOptions();
20
36
  }
21
37
  function clearCreatedOptions() {
22
38
  createdOptions.value = [];
39
+ resetToSourceOptions();
23
40
  }
24
41
  const setTempOption = (qs, options) => {
25
42
  if (!qs?.trim() || !props.creatable) {
@@ -28,9 +45,10 @@ function useOptions(o$1) {
28
45
  }
29
46
  const labelKey = fieldKey(props.labelKey, "label");
30
47
  const valueKey = fieldKey(props.valueKey, "value");
31
- if (!(options ? options.some((option) => {
48
+ const exactMatch = options ? options.some((option) => {
32
49
  return o(option).get(labelKey) === qs;
33
- }) : false)) tempOptions.value = [{
50
+ }) : false;
51
+ tempOptions.value = exactMatch ? [] : [{
34
52
  [labelKey]: qs,
35
53
  [valueKey]: qs,
36
54
  __isTemp: true
@@ -39,35 +57,58 @@ function useOptions(o$1) {
39
57
  const allOptions = computed(() => {
40
58
  const { options } = props;
41
59
  if (typeof options === "function") return remoteOptions.value;
42
- return options ?? [];
60
+ return [...createdOptions.value, ...options ?? []];
43
61
  });
44
- /** 展示的选项 */
62
+ /**
63
+ * 展示的选项
64
+ * @description 已创建选项并入过滤数据源参与匹配;临时选项(待创建)固定置顶
65
+ */
45
66
  const options = computed(() => {
46
67
  const { options } = props;
47
68
  if (typeof options === "function") return remoteOptions.value;
48
- const prependOptions = tempOptions.value?.length ? tempOptions.value : createdOptions.value;
49
- if (!options) return prependOptions;
50
- return [...prependOptions, ...filteredOptions.value];
69
+ return [...tempOptions.value, ...filteredOptions.value];
51
70
  });
52
- watch([queryString, () => props.options], debounce(async ([qs, propsOptions]) => {
71
+ const applyLocalFilter = (qs, propsOptions) => {
72
+ const labelKey = fieldKey(props.labelKey, "label");
73
+ const sourceOptions = [...createdOptions.value, ...propsOptions ?? []];
74
+ if (!qs) {
75
+ filteredOptions.value = sourceOptions;
76
+ setTempOption(qs, filteredOptions.value);
77
+ return;
78
+ }
79
+ const _filteredOptions = sourceOptions.filter((item) => {
80
+ return o(item).get(labelKey)?.includes(qs) ?? false;
81
+ });
82
+ setTempOption(qs, _filteredOptions);
83
+ filteredOptions.value = _filteredOptions;
84
+ };
85
+ const debouncedLocalFilter = debounce((qs, propsOptions) => {
86
+ if (!queryString.value) {
87
+ applyLocalFilter("", propsOptions);
88
+ return;
89
+ }
90
+ applyLocalFilter(qs, propsOptions);
91
+ }, 200);
92
+ const debouncedRemoteFilter = debounce(async (qs, propsOptions) => {
93
+ const options = await propsOptions(queryString.value);
94
+ remoteOptions.value = options;
95
+ setTempOption(queryString.value, options);
96
+ }, 200);
97
+ watch([
98
+ queryString,
99
+ () => props.options,
100
+ createdOptions
101
+ ], ([qs, propsOptions]) => {
53
102
  if (typeof propsOptions === "function") {
54
- const options = await propsOptions(qs);
55
- remoteOptions.value = options;
56
- setTempOption(qs, options);
57
- } else {
58
- const labelKey = fieldKey(props.labelKey, "label");
59
- if (!qs) {
60
- filteredOptions.value = propsOptions ?? [];
61
- setTempOption(qs, filteredOptions.value);
62
- return;
63
- }
64
- const _filteredOptions = propsOptions?.filter((item) => {
65
- return o(item).get(labelKey)?.includes(qs) ?? false;
66
- }) ?? [];
67
- setTempOption(qs, _filteredOptions);
68
- filteredOptions.value = _filteredOptions;
103
+ debouncedRemoteFilter(qs, propsOptions);
104
+ return;
105
+ }
106
+ if (!qs) {
107
+ applyLocalFilter("", propsOptions);
108
+ return;
69
109
  }
70
- }, 200), { immediate: true });
110
+ debouncedLocalFilter(qs, propsOptions);
111
+ }, { immediate: true });
71
112
  return {
72
113
  queryString,
73
114
  options,