@veltra/desktop 1.7.18 → 1.8.0

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 (82) hide show
  1. package/dist/components/batch-edit/batch-edit-form.js +40 -26
  2. package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
  3. package/dist/components/batch-edit/batch-edit-list.js +17 -5
  4. package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
  5. package/dist/components/batch-edit/di.js.map +1 -1
  6. package/dist/components/batch-edit/use-handlers.js +3 -3
  7. package/dist/components/batch-edit/use-handlers.js.map +1 -1
  8. package/dist/components/batch-edit/use-shortcut-key.js +4 -2
  9. package/dist/components/batch-edit/use-shortcut-key.js.map +1 -1
  10. package/dist/components/card/style2.css +0 -2
  11. package/dist/components/dialog/style2.css +1 -1
  12. package/dist/components/dropdown/dropdown.js +5 -3
  13. package/dist/components/dropdown/dropdown.js.map +1 -1
  14. package/dist/components/expression-editor/components/path-preview.js +3 -6
  15. package/dist/components/expression-editor/components/path-preview.js.map +1 -1
  16. package/dist/components/expression-editor/components/variable-picker.js +49 -12
  17. package/dist/components/expression-editor/components/variable-picker.js.map +1 -1
  18. package/dist/components/expression-editor/core/chip.js +1 -15
  19. package/dist/components/expression-editor/core/chip.js.map +1 -1
  20. package/dist/components/expression-editor/core/editor.js +20 -18
  21. package/dist/components/expression-editor/core/editor.js.map +1 -1
  22. package/dist/components/expression-editor/expression-editor.js +53 -22
  23. package/dist/components/expression-editor/expression-editor.js.map +1 -1
  24. package/dist/components/expression-editor/style2.css +135 -96
  25. package/dist/components/form/form.js.map +1 -1
  26. package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js +0 -1
  27. package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js.map +1 -1
  28. package/dist/components/form/style2.css +8 -3
  29. package/dist/components/index.d.ts +26 -26
  30. package/dist/components/index.js +52 -52
  31. package/dist/components/multi-select/multi-select.js +16 -12
  32. package/dist/components/multi-select/multi-select.js.map +1 -1
  33. package/dist/components/multi-select/style2.css +7 -0
  34. package/dist/components/multi-tree-select/multi-tree-select.js +82 -56
  35. package/dist/components/multi-tree-select/multi-tree-select.js.map +1 -1
  36. package/dist/components/multi-tree-select/style2.css +7 -0
  37. package/dist/components/select/select.js +7 -3
  38. package/dist/components/select/select.js.map +1 -1
  39. package/dist/components/select/style2.css +7 -0
  40. package/dist/components/select/use-options.js +14 -3
  41. package/dist/components/select/use-options.js.map +1 -1
  42. package/dist/components/tree-select/style2.css +7 -0
  43. package/dist/components/tree-select/tree-select.js +49 -17
  44. package/dist/components/tree-select/tree-select.js.map +1 -1
  45. package/dist/index.d.ts +1 -1
  46. package/dist/index.js +2 -2
  47. package/dist/types/dropdown.d.ts +10 -0
  48. package/dist/types/multi-tree-select.d.ts +7 -1
  49. package/dist/types/tree-select.d.ts +7 -1
  50. package/package.json +1 -1
  51. package/src/components/batch-edit/__test__/batch-edit.test.ts +119 -7
  52. package/src/components/batch-edit/batch-edit-form.vue +21 -2
  53. package/src/components/batch-edit/batch-edit-list.vue +18 -1
  54. package/src/components/batch-edit/di.ts +1 -1
  55. package/src/components/batch-edit/use-handlers.ts +6 -5
  56. package/src/components/batch-edit/use-shortcut-key.ts +5 -2
  57. package/src/components/card/style.scss +0 -2
  58. package/src/components/dialog/style.scss +2 -1
  59. package/src/components/dropdown/dropdown.vue +9 -4
  60. package/src/components/expression-editor/__test__/chip.test.ts +3 -14
  61. package/src/components/expression-editor/components/path-preview.vue +1 -2
  62. package/src/components/expression-editor/components/variable-picker.vue +54 -13
  63. package/src/components/expression-editor/core/chip.ts +3 -29
  64. package/src/components/expression-editor/core/editor.ts +28 -21
  65. package/src/components/expression-editor/expression-editor.vue +47 -21
  66. package/src/components/expression-editor/style.scss +167 -111
  67. package/src/components/form/form.vue +0 -1
  68. package/src/components/form/style.scss +11 -4
  69. package/src/components/multi-select/multi-select.vue +11 -2
  70. package/src/components/multi-select/style.scss +6 -0
  71. package/src/components/multi-tree-select/multi-tree-select.vue +58 -9
  72. package/src/components/multi-tree-select/style.scss +6 -0
  73. package/src/components/select/__test__/select.test.ts +78 -0
  74. package/src/components/select/select.vue +13 -3
  75. package/src/components/select/style.scss +6 -0
  76. package/src/components/select/use-options.ts +25 -4
  77. package/src/components/tree-select/__test__/tree-select.test.ts +116 -0
  78. package/src/components/tree-select/style.scss +6 -0
  79. package/src/components/tree-select/tree-select.vue +63 -13
  80. package/src/types/dropdown.ts +10 -0
  81. package/src/types/multi-tree-select.ts +12 -1
  82. package/src/types/tree-select.ts +12 -1
@@ -1,14 +1,15 @@
1
1
  import _sfc_main$1 from "../icon/icon.js";
2
2
  import _sfc_main$2 from "../dropdown/dropdown.js";
3
- import _sfc_main$3 from "../checkbox/checkbox.js";
4
- import _sfc_main$4 from "../tree/tree.js";
5
- import _sfc_main$5 from "../button/button.js";
6
- import _sfc_main$6 from "../tag/tag.js";
3
+ import _sfc_main$3 from "../loading/loading.js";
4
+ import _sfc_main$4 from "../checkbox/checkbox.js";
5
+ import _sfc_main$5 from "../tree/tree.js";
6
+ import _sfc_main$6 from "../button/button.js";
7
+ import _sfc_main$7 from "../tag/tag.js";
7
8
  import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeModels, mergeProps, nextTick, normalizeClass, openBlock, renderList, shallowRef, toDisplayString, unref, useModel, useSlots, useTemplateRef, vModelText, watch, withCtx, withDirectives, withModifiers } from "vue";
8
9
  import { useFormFallbackProps } from "@veltra/compositions";
9
10
  import { FORM_EMPTY_CONTENT, bem, fieldKey, injectFormContext } from "@veltra/utils";
10
11
  import { ArrowDown, Close } from "@veltra/icons/normal";
11
- import { dfs, o } from "@cat-kit/core";
12
+ import { debounce, dfs, o } from "@cat-kit/core";
12
13
  //#region src/components/multi-tree-select/multi-tree-select.vue
13
14
  const _hoisted_1 = ["placeholder", "disabled"];
14
15
  const _sfc_main = /*@__PURE__*/ defineComponent({
@@ -17,6 +18,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
17
18
  __name: "multi-tree-select",
18
19
  props: /*@__PURE__*/ mergeModels({
19
20
  modelValue: {},
21
+ data: {},
20
22
  placeholder: { default: "请选择" },
21
23
  clearable: {
22
24
  type: Boolean,
@@ -53,7 +55,6 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
53
55
  labelKey: { default: "label" },
54
56
  valueKey: { default: "value" },
55
57
  childrenKey: {},
56
- data: {},
57
58
  disabledNode: {},
58
59
  checkOnClickNode: {
59
60
  type: Boolean,
@@ -88,6 +89,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
88
89
  "label",
89
90
  "readonly",
90
91
  "modelValue",
92
+ "data",
91
93
  "contentClass",
92
94
  "contentStyle",
93
95
  "minWidth",
@@ -96,11 +98,37 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
96
98
  });
97
99
  const emit = __emit;
98
100
  const slots = useSlots();
99
- /**过滤 */
101
+ /** 是否可搜索:显式声明或数据源为远程函数 */
102
+ const filterable = computed(() => props.filterable || typeof props.data === "function");
103
+ /** 过滤 */
100
104
  const qs = shallowRef("");
101
- watch(qs, (qs) => {
102
- treeRef.value?.filter(qs);
103
- });
105
+ const treeRef = shallowRef();
106
+ /** 远程搜索返回的树数据 */
107
+ const remoteData = shallowRef();
108
+ /** 面板渲染的树数据:data 为函数时来自远程搜索,否则为静态数据 */
109
+ const treeData = computed(() => typeof props.data === "function" ? remoteData.value : props.data);
110
+ const remoteLoading = shallowRef(false);
111
+ /** 远程请求序号;响应返回时序号不一致说明已有更新的请求,结果作废 */
112
+ let remoteSeq = 0;
113
+ const debouncedRemoteSearch = debounce(async (query, dataFn) => {
114
+ const seq = ++remoteSeq;
115
+ remoteLoading.value = true;
116
+ try {
117
+ const data = await dataFn(query);
118
+ if (seq !== remoteSeq || query !== qs.value) return;
119
+ remoteData.value = data ?? [];
120
+ if (query) nextTick(() => treeRef.value?.expandAll());
121
+ } finally {
122
+ if (seq === remoteSeq) remoteLoading.value = false;
123
+ }
124
+ }, 200);
125
+ watch([qs, () => props.data], ([query, data]) => {
126
+ if (typeof data === "function") {
127
+ debouncedRemoteSearch(query, data);
128
+ return;
129
+ }
130
+ treeRef.value?.filter(query);
131
+ }, { immediate: true });
104
132
  const model = useModel(__props, "modelValue");
105
133
  const hovered = shallowRef(false);
106
134
  const tags = shallowRef([]);
@@ -109,7 +137,6 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
109
137
  const showClear = computed(() => {
110
138
  return props.clearable && model.value?.length && hovered.value && !disabled.value;
111
139
  });
112
- const treeRef = shallowRef();
113
140
  const dropdownRef = shallowRef();
114
141
  let changedByEvent = false;
115
142
  function markEvent() {
@@ -153,7 +180,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
153
180
  emit("clear");
154
181
  };
155
182
  const keyDicts = shallowRef(/* @__PURE__ */ new Map());
156
- watch(() => props.data, (data) => {
183
+ watch(treeData, (data) => {
157
184
  if (!data?.length) keyDicts.value = /* @__PURE__ */ new Map();
158
185
  else {
159
186
  const newDict = /* @__PURE__ */ new Map();
@@ -237,17 +264,17 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
237
264
  }), {
238
265
  trigger: withCtx(() => [
239
266
  createCommentVNode(" 默认展示 "),
240
- !__props.filterable && !tags.value.length ? (openBlock(), createElementBlock("span", {
267
+ !filterable.value && !tags.value.length ? (openBlock(), createElementBlock("span", {
241
268
  key: 0,
242
269
  class: normalizeClass(unref(cls).e("placeholder"))
243
270
  }, toDisplayString(__props.placeholder), 3)) : createCommentVNode("v-if", true),
244
271
  createCommentVNode(" 选择的数据项 "),
245
- tags.value.length || __props.filterable ? (openBlock(), createElementBlock("div", {
272
+ tags.value.length || filterable.value ? (openBlock(), createElementBlock("div", {
246
273
  key: 1,
247
274
  class: normalizeClass(unref(cls).e("tags"))
248
275
  }, [
249
276
  (openBlock(true), createElementBlock(Fragment, null, renderList(visibleTags.value, (tag, index) => {
250
- return openBlock(), createBlock(unref(_sfc_main$6), {
277
+ return openBlock(), createBlock(unref(_sfc_main$7), {
251
278
  key: tag[valueKey.value],
252
279
  closable: !unref(disabled),
253
280
  onClose: ($event) => handleRemove(index)
@@ -256,12 +283,12 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
256
283
  _: 2
257
284
  }, 1032, ["closable", "onClose"]);
258
285
  }), 128)),
259
- hiddenCount.value > 0 ? (openBlock(), createBlock(unref(_sfc_main$6), { key: 0 }, {
286
+ hiddenCount.value > 0 ? (openBlock(), createBlock(unref(_sfc_main$7), { key: 0 }, {
260
287
  default: withCtx(() => [createTextVNode("+" + toDisplayString(hiddenCount.value), 1)]),
261
288
  _: 1
262
289
  })) : createCommentVNode("v-if", true),
263
290
  createCommentVNode(" 内嵌查询输入框:承载过滤 "),
264
- __props.filterable ? withDirectives((openBlock(), createElementBlock("input", {
291
+ filterable.value ? withDirectives((openBlock(), createElementBlock("input", {
265
292
  key: 1,
266
293
  ref_key: "inputRef",
267
294
  ref: inputRef,
@@ -295,44 +322,43 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
295
322
  _: 1
296
323
  })
297
324
  ]),
298
- content: withCtx(() => [
299
- createElementVNode("div", { class: normalizeClass([unref(cls).e("content-header"), unref(cls).m(unref(size))]) }, [
300
- createCommentVNode(" 全选 "),
301
- createVNode(unref(_sfc_main$3), {
302
- "model-value": allChecked.value,
303
- indeterminate: indeterminate.value,
304
- "onUpdate:modelValue": handleCheckAll
305
- }, {
306
- default: withCtx(() => [..._cache[4] || (_cache[4] = [createTextVNode(" 全选 ", -1)])]),
307
- _: 1
308
- }, 8, ["model-value", "indeterminate"]),
309
- createVNode(unref(_sfc_main$5), {
310
- size: "small",
311
- text: "",
312
- type: "primary",
313
- onClick: handleToggleExpandAll
314
- }, {
315
- default: withCtx(() => [createTextVNode(toDisplayString(allExpanded.value ? "收起全部" : "展开全部"), 1)]),
316
- _: 1
317
- })
318
- ], 2),
319
- createCommentVNode(" 菜单列表 "),
320
- createVNode(unref(_sfc_main$4), mergeProps(treeProps.value, {
321
- checked: model.value,
322
- "onUpdate:checked": [_cache[1] || (_cache[1] = ($event) => model.value = $event), handleCheck],
323
- ref_key: "treeRef",
324
- ref: treeRef,
325
- class: unref(cls).e("content-tree"),
326
- checkable: "",
327
- data: __props.data,
328
- slots,
329
- "scroll-to-view": ""
330
- }), null, 16, [
331
- "checked",
332
- "class",
333
- "data"
334
- ])
335
- ]),
325
+ content: withCtx(() => [createElementVNode("div", { class: normalizeClass([unref(cls).e("content-header"), unref(cls).m(unref(size))]) }, [
326
+ createCommentVNode(" 全选 "),
327
+ createVNode(unref(_sfc_main$4), {
328
+ "model-value": allChecked.value,
329
+ indeterminate: indeterminate.value,
330
+ "onUpdate:modelValue": handleCheckAll
331
+ }, {
332
+ default: withCtx(() => [..._cache[4] || (_cache[4] = [createTextVNode(" 全选 ", -1)])]),
333
+ _: 1
334
+ }, 8, ["model-value", "indeterminate"]),
335
+ createVNode(unref(_sfc_main$6), {
336
+ size: "small",
337
+ text: "",
338
+ type: "primary",
339
+ onClick: handleToggleExpandAll
340
+ }, {
341
+ default: withCtx(() => [createTextVNode(toDisplayString(allExpanded.value ? "收起全部" : "展开全部"), 1)]),
342
+ _: 1
343
+ })
344
+ ], 2), remoteLoading.value ? (openBlock(), createElementBlock("div", {
345
+ key: 0,
346
+ class: normalizeClass(unref(cls).e("loading"))
347
+ }, [createVNode(unref(_sfc_main$3), { type: "dual-ring" })], 2)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createCommentVNode(" 菜单列表 "), createVNode(unref(_sfc_main$5), mergeProps(treeProps.value, {
348
+ checked: model.value,
349
+ "onUpdate:checked": [_cache[1] || (_cache[1] = ($event) => model.value = $event), handleCheck],
350
+ ref_key: "treeRef",
351
+ ref: treeRef,
352
+ class: unref(cls).e("content-tree"),
353
+ checkable: "",
354
+ data: treeData.value,
355
+ slots,
356
+ "scroll-to-view": ""
357
+ }), null, 16, [
358
+ "checked",
359
+ "class",
360
+ "data"
361
+ ])], 2112))]),
336
362
  _: 1
337
363
  }, 16, [
338
364
  "class",
@@ -345,7 +371,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
345
371
  key: 1,
346
372
  class: normalizeClass([unref(cls).m(unref(size)), unref(cls).e("readonly-tags")])
347
373
  }, [createElementVNode("div", { class: normalizeClass(unref(cls).e("tags")) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(tags.value, (option) => {
348
- return openBlock(), createBlock(unref(_sfc_main$6), { key: option[valueKey.value] }, {
374
+ return openBlock(), createBlock(unref(_sfc_main$7), { key: option[valueKey.value] }, {
349
375
  default: withCtx(() => [createTextVNode(toDisplayString(option[labelKey.value]), 1)]),
350
376
  _: 2
351
377
  }, 1024);
@@ -1 +1 @@
1
- {"version":3,"file":"multi-tree-select.js","names":["$attrs"],"sources":["../../../src/components/multi-tree-select/multi-tree-select.vue"],"sourcesContent":["<template>\n <u-dropdown\n v-if=\"!readonly\"\n v-bind=\"$attrs\"\n :class=\"[cls.b, cls.m(size), bem.is('disabled', disabled), bem.is('focus', dropdownVisible)]\"\n trigger=\"click\"\n :content-class=\"[cls.e('panel'), cls.em('panel', size), contentClass]\"\n :content-style=\"contentStyle\"\n :disabled=\"disabled\"\n @mouseenter=\"hovered = true\"\n @mouseleave=\"hovered = false\"\n ref=\"dropdownRef\"\n :min-width=\"minWidth\"\n :width=\"width\"\n @update:visible=\"handleDropdownVisible\"\n >\n <template #trigger>\n <!-- 默认展示 -->\n <span :class=\"cls.e('placeholder')\" v-if=\"!filterable && !tags.length\">\n {{ placeholder }}\n </span>\n\n <!-- 选择的数据项 -->\n <div v-if=\"tags.length || filterable\" :class=\"cls.e('tags')\">\n <u-tag\n v-for=\"(tag, index) in visibleTags\"\n :key=\"tag[valueKey]\"\n :closable=\"!disabled\"\n @close=\"handleRemove(index)\"\n >\n {{ tag[labelKey] }}\n </u-tag>\n <u-tag v-if=\"hiddenCount > 0\">+{{ hiddenCount }}</u-tag>\n\n <!-- 内嵌查询输入框:承载过滤 -->\n <input\n v-if=\"filterable\"\n ref=\"inputRef\"\n :class=\"cls.e('input')\"\n v-model=\"qs\"\n :placeholder=\"tags.length ? '' : placeholder\"\n :disabled=\"disabled\"\n @click.stop=\"handleInputClick\"\n @focus=\"handleInputFocus\"\n />\n </div>\n <!-- 清空 icon -->\n <transition name=\"zoom-in\" mode=\"out-in\">\n <u-icon v-if=\"showClear\" :class=\"cls.e('clear')\" @click.stop=\"handleClear\">\n <Close />\n </u-icon>\n\n <!-- 下拉 icon -->\n <u-icon :class=\"cls.e(`arrow`)\" v-else-if=\"!readonly\">\n <ArrowDown />\n </u-icon>\n </transition>\n </template>\n <template #content>\n <div :class=\"[cls.e('content-header'), cls.m(size)]\">\n <!-- 全选 -->\n <u-checkbox\n :model-value=\"allChecked\"\n :indeterminate=\"indeterminate\"\n @update:model-value=\"handleCheckAll\"\n >\n 全选\n </u-checkbox>\n\n <u-button size=\"small\" text type=\"primary\" @click=\"handleToggleExpandAll\">\n {{ allExpanded ? '收起全部' : '展开全部' }}\n </u-button>\n </div>\n <!-- 菜单列表 -->\n <u-tree\n v-bind=\"treeProps\"\n v-model:checked=\"model\"\n @update:checked=\"handleCheck\"\n ref=\"treeRef\"\n :class=\"cls.e('content-tree')\"\n checkable\n :data=\"data\"\n :slots=\"slots\"\n scroll-to-view\n ></u-tree>\n </template>\n </u-dropdown>\n\n <div v-else-if=\"model.length\" :class=\"[cls.m(size), cls.e('readonly-tags')]\">\n <div :class=\"cls.e('tags')\">\n <u-tag v-for=\"option of tags\" :key=\"option[valueKey]\">\n {{ option[labelKey] }}\n </u-tag>\n </div>\n </div>\n\n <template v-else>\n {{ FORM_EMPTY_CONTENT }}\n </template>\n</template>\n\n<script lang=\"ts\" setup>\nimport { dfs, o } from '@cat-kit/core'\nimport { useFormFallbackProps } from '@veltra/compositions'\nimport { ArrowDown, Close } from '@veltra/icons/normal'\nimport { bem, fieldKey, FORM_EMPTY_CONTENT } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { computed, nextTick, shallowRef, useTemplateRef, watch } from 'vue'\n\nimport type { MultiTreeSelectProps, MultiTreeSelectEmits, TreeExposed } from '../../types'\nimport type { DropdownExposed } from '../../types'\nimport { UButton } from '../button'\nimport { UCheckbox } from '../checkbox'\nimport { UDropdown } from '../dropdown'\nimport { UIcon } from '../icon'\nimport { UTag } from '../tag'\nimport { UTree } from '../tree'\nimport type { TreeSlotsScope } from '../tree/di'\n\ndefineOptions({ name: 'UMultiTreeSelect', inheritAttrs: false })\n\nconst cls = bem('multi-tree-select')\n\nconst props = withDefaults(defineProps<MultiTreeSelectProps>(), {\n labelKey: 'label',\n valueKey: 'value',\n placeholder: '请选择',\n expandAll: false,\n clearable: true,\n disabled: undefined,\n readonly: undefined,\n filterable: false,\n visibilityLimit: 3,\n minWidth: '280px',\n checkStrictly: false,\n checkOnClickNode: true\n})\n\nconst labelKey = computed(() => fieldKey(props.labelKey, 'label'))\nconst valueKey = computed(() => fieldKey(props.valueKey, 'value'))\n\nconst treeProps = computed(() => {\n return o(props as Record<string, any>).omit([\n 'tips',\n 'field',\n 'placeholder',\n 'disabled',\n 'label',\n 'readonly',\n 'modelValue',\n 'contentClass',\n 'contentStyle',\n 'minWidth',\n 'width'\n ])\n})\n\nconst emit = defineEmits<MultiTreeSelectEmits>()\n\nconst slots = defineSlots<{ default?: (props: TreeSlotsScope) => any }>()\n\n/**过滤 */\nconst qs = shallowRef('')\nwatch(qs, (qs) => {\n treeRef.value?.filter(qs)\n})\n\nconst model = defineModel<(string | number)[]>({ default: () => [] })\n\nconst hovered = shallowRef(false)\n\nconst tags = shallowRef<Record<string, any>[]>([])\n\nconst { formProps } = injectFormContext()\n\nconst { size, disabled, readonly } = useFormFallbackProps([formProps ?? {}, props])\n\nconst showClear = computed(() => {\n return props.clearable && model.value?.length && hovered.value && !disabled.value\n})\n\nconst treeRef = shallowRef<TreeExposed>()\n\nconst dropdownRef = shallowRef<DropdownExposed>()\n\nlet changedByEvent = false\n\nfunction markEvent() {\n changedByEvent = true\n nextTick(() => {\n changedByEvent = false\n })\n}\n\n/**是否全选 */\nconst allChecked = computed(() => {\n return model.value?.length === treeRef.value?.forest.size\n})\n\n/**部分 */\nconst indeterminate = computed(() => {\n return model.value.length! > 0 && !allChecked.value\n})\n\n/** 全选*/\nconst handleCheckAll = (checked: boolean) => {\n treeRef.value?.checkAll(checked)\n\n dropdownRef.value?.updateDropdown()\n}\n\n/**选中 */\nconst handleCheck = (checked: (string | number)[], checkedData: Record<string, any>[]) => {\n markEvent()\n tags.value = checkedData\n emit('change', checkedData!)\n\n dropdownRef.value?.updateDropdown()\n}\n\n/**删除 */\nconst handleRemove = (index: number) => {\n markEvent()\n tags.value = tags.value.filter((_, i) => i !== index)\n model.value = model.value.filter((_, i) => i !== index)\n emit('change', tags.value)\n}\n\n/**清空 */\nconst handleClear = () => {\n markEvent()\n tags.value = []\n model.value = []\n emit('clear')\n}\n\nconst keyDicts = shallowRef(new Map<string | number, Record<string, any>>())\n\nwatch(\n () => props.data,\n (data) => {\n if (!data?.length) {\n keyDicts.value = new Map()\n } else {\n const newDict = new Map()\n data.forEach((item) => {\n dfs(\n item,\n (v) => {\n newDict.set(v[valueKey.value], v)\n },\n props.childrenKey\n )\n })\n keyDicts.value = newDict\n }\n },\n { immediate: true }\n)\n\nwatch(\n [keyDicts, model],\n ([keyDicts, model]) => {\n if (changedByEvent) return\n\n if (!keyDicts.size || !model?.length) {\n tags.value = []\n return\n }\n\n tags.value = model.filter((v) => keyDicts.has(v)).map((v) => keyDicts.get(v)!)\n },\n { immediate: true }\n)\n\nconst allExpanded = shallowRef(props.expandAll)\nfunction handleToggleExpandAll() {\n allExpanded.value = !allExpanded.value\n allExpanded.value ? treeRef.value?.expandAll() : treeRef.value?.collapseAll()\n}\n\nwatch(treeRef, (treeRef) => {\n if (!treeRef) {\n allExpanded.value = props.expandAll\n }\n})\n\nconst limit = () => {\n let { visibilityLimit } = props\n\n if (visibilityLimit < 0) {\n visibilityLimit = 0\n }\n if (disabled.value || readonly.value) {\n visibilityLimit = model.value?.length ?? 0\n }\n return visibilityLimit\n}\n\nconst visibleTags = computed(() => {\n if (tags.value.length > limit()) {\n return tags.value.slice(0, limit())\n }\n return tags.value\n})\n\nconst hiddenCount = computed(() => {\n if (tags.value.length > limit()) {\n return tags.value.length - limit()\n }\n return 0\n})\n\nconst dropdownVisible = shallowRef(false)\n\nconst inputRef = useTemplateRef<HTMLInputElement>('inputRef')\n\nconst handleDropdownVisible = (visible: boolean) => {\n dropdownVisible.value = visible\n if (!visible) {\n qs.value = ''\n }\n}\n\n/**\n * 拦截输入区域的点击(@click.stop):阻止 dropdown 的 trigger 开合切换,\n * 保持面板展开以不中断输入。\n */\nconst handleInputClick = () => {\n if (!dropdownVisible.value) dropdownRef.value?.open()\n}\n\nconst handleInputFocus = () => {\n if (!dropdownVisible.value) dropdownRef.value?.open()\n}\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"multi-tree-select.js","names":["$attrs"],"sources":["../../../src/components/multi-tree-select/multi-tree-select.vue"],"sourcesContent":["<template>\n <u-dropdown\n v-if=\"!readonly\"\n v-bind=\"$attrs\"\n :class=\"[cls.b, cls.m(size), bem.is('disabled', disabled), bem.is('focus', dropdownVisible)]\"\n trigger=\"click\"\n :content-class=\"[cls.e('panel'), cls.em('panel', size), contentClass]\"\n :content-style=\"contentStyle\"\n :disabled=\"disabled\"\n @mouseenter=\"hovered = true\"\n @mouseleave=\"hovered = false\"\n ref=\"dropdownRef\"\n :min-width=\"minWidth\"\n :width=\"width\"\n @update:visible=\"handleDropdownVisible\"\n >\n <template #trigger>\n <!-- 默认展示 -->\n <span :class=\"cls.e('placeholder')\" v-if=\"!filterable && !tags.length\">\n {{ placeholder }}\n </span>\n\n <!-- 选择的数据项 -->\n <div v-if=\"tags.length || filterable\" :class=\"cls.e('tags')\">\n <u-tag\n v-for=\"(tag, index) in visibleTags\"\n :key=\"tag[valueKey]\"\n :closable=\"!disabled\"\n @close=\"handleRemove(index)\"\n >\n {{ tag[labelKey] }}\n </u-tag>\n <u-tag v-if=\"hiddenCount > 0\">+{{ hiddenCount }}</u-tag>\n\n <!-- 内嵌查询输入框:承载过滤 -->\n <input\n v-if=\"filterable\"\n ref=\"inputRef\"\n :class=\"cls.e('input')\"\n v-model=\"qs\"\n :placeholder=\"tags.length ? '' : placeholder\"\n :disabled=\"disabled\"\n @click.stop=\"handleInputClick\"\n @focus=\"handleInputFocus\"\n />\n </div>\n <!-- 清空 icon -->\n <transition name=\"zoom-in\" mode=\"out-in\">\n <u-icon v-if=\"showClear\" :class=\"cls.e('clear')\" @click.stop=\"handleClear\">\n <Close />\n </u-icon>\n\n <!-- 下拉 icon -->\n <u-icon :class=\"cls.e(`arrow`)\" v-else-if=\"!readonly\">\n <ArrowDown />\n </u-icon>\n </transition>\n </template>\n <template #content>\n <div :class=\"[cls.e('content-header'), cls.m(size)]\">\n <!-- 全选 -->\n <u-checkbox\n :model-value=\"allChecked\"\n :indeterminate=\"indeterminate\"\n @update:model-value=\"handleCheckAll\"\n >\n 全选\n </u-checkbox>\n\n <u-button size=\"small\" text type=\"primary\" @click=\"handleToggleExpandAll\">\n {{ allExpanded ? '收起全部' : '展开全部' }}\n </u-button>\n </div>\n <div v-if=\"remoteLoading\" :class=\"cls.e('loading')\">\n <ULoading type=\"dual-ring\" />\n </div>\n <!-- 菜单列表 -->\n <u-tree\n v-else\n v-bind=\"treeProps\"\n v-model:checked=\"model\"\n @update:checked=\"handleCheck\"\n ref=\"treeRef\"\n :class=\"cls.e('content-tree')\"\n checkable\n :data=\"treeData\"\n :slots=\"slots\"\n scroll-to-view\n ></u-tree>\n </template>\n </u-dropdown>\n\n <div v-else-if=\"model.length\" :class=\"[cls.m(size), cls.e('readonly-tags')]\">\n <div :class=\"cls.e('tags')\">\n <u-tag v-for=\"option of tags\" :key=\"option[valueKey]\">\n {{ option[labelKey] }}\n </u-tag>\n </div>\n </div>\n\n <template v-else>\n {{ FORM_EMPTY_CONTENT }}\n </template>\n</template>\n\n<script lang=\"ts\" setup>\nimport { debounce, dfs, o } from '@cat-kit/core'\nimport { useFormFallbackProps } from '@veltra/compositions'\nimport { ArrowDown, Close } from '@veltra/icons/normal'\nimport { bem, fieldKey, FORM_EMPTY_CONTENT } from '@veltra/utils'\nimport { injectFormContext } from '@veltra/utils'\nimport { computed, nextTick, shallowRef, useTemplateRef, watch } from 'vue'\n\nimport type { MultiTreeSelectProps, MultiTreeSelectEmits, TreeExposed } from '../../types'\nimport type { DropdownExposed } from '../../types'\nimport { UButton } from '../button'\nimport { UCheckbox } from '../checkbox'\nimport { UDropdown } from '../dropdown'\nimport { UIcon } from '../icon'\nimport { ULoading } from '../loading'\nimport { UTag } from '../tag'\nimport { UTree } from '../tree'\nimport type { TreeSlotsScope } from '../tree/di'\n\ndefineOptions({ name: 'UMultiTreeSelect', inheritAttrs: false })\n\nconst cls = bem('multi-tree-select')\n\nconst props = withDefaults(defineProps<MultiTreeSelectProps>(), {\n labelKey: 'label',\n valueKey: 'value',\n placeholder: '请选择',\n expandAll: false,\n clearable: true,\n disabled: undefined,\n readonly: undefined,\n filterable: false,\n visibilityLimit: 3,\n minWidth: '280px',\n checkStrictly: false,\n checkOnClickNode: true\n})\n\nconst labelKey = computed(() => fieldKey(props.labelKey, 'label'))\nconst valueKey = computed(() => fieldKey(props.valueKey, 'value'))\n\nconst treeProps = computed(() => {\n return o(props as Record<string, any>).omit([\n 'tips',\n 'field',\n 'placeholder',\n 'disabled',\n 'label',\n 'readonly',\n 'modelValue',\n 'data',\n 'contentClass',\n 'contentStyle',\n 'minWidth',\n 'width'\n ])\n})\n\nconst emit = defineEmits<MultiTreeSelectEmits>()\n\nconst slots = defineSlots<{ default?: (props: TreeSlotsScope) => any }>()\n\n/** 是否可搜索:显式声明或数据源为远程函数 */\nconst filterable = computed(() => props.filterable || typeof props.data === 'function')\n\n/** 过滤 */\nconst qs = shallowRef('')\n\nconst treeRef = shallowRef<TreeExposed>()\n\n/** 远程搜索返回的树数据 */\nconst remoteData = shallowRef<Record<string, any>[]>()\n\n/** 面板渲染的树数据:data 为函数时来自远程搜索,否则为静态数据 */\nconst treeData = computed(() => (typeof props.data === 'function' ? remoteData.value : props.data))\n\nconst remoteLoading = shallowRef(false)\n/** 远程请求序号;响应返回时序号不一致说明已有更新的请求,结果作废 */\nlet remoteSeq = 0\n\nconst debouncedRemoteSearch = debounce(\n async (\n query: string,\n dataFn: (qs: string) => Promise<Record<string, any>[]> | Record<string, any>[]\n ) => {\n const seq = ++remoteSeq\n remoteLoading.value = true\n try {\n const data = await dataFn(query)\n // 响应到达时查询词可能已变化或已有更新的请求,过期结果直接丢弃\n if (seq !== remoteSeq || query !== qs.value) return\n remoteData.value = data ?? []\n // 搜索结果展示完整命中路径,与本地过滤自动展开祖先的行为对齐\n if (query) nextTick(() => treeRef.value?.expandAll())\n } finally {\n if (seq === remoteSeq) remoteLoading.value = false\n }\n },\n 200\n)\n\nwatch(\n [qs, () => props.data],\n ([query, data]) => {\n if (typeof data === 'function') {\n debouncedRemoteSearch(query, data)\n return\n }\n treeRef.value?.filter(query)\n },\n { immediate: true }\n)\n\nconst model = defineModel<(string | number)[]>({ default: () => [] })\n\nconst hovered = shallowRef(false)\n\nconst tags = shallowRef<Record<string, any>[]>([])\n\nconst { formProps } = injectFormContext()\n\nconst { size, disabled, readonly } = useFormFallbackProps([formProps ?? {}, props])\n\nconst showClear = computed(() => {\n return props.clearable && model.value?.length && hovered.value && !disabled.value\n})\n\nconst dropdownRef = shallowRef<DropdownExposed>()\n\nlet changedByEvent = false\n\nfunction markEvent() {\n changedByEvent = true\n nextTick(() => {\n changedByEvent = false\n })\n}\n\n/**是否全选 */\nconst allChecked = computed(() => {\n return model.value?.length === treeRef.value?.forest.size\n})\n\n/**部分 */\nconst indeterminate = computed(() => {\n return model.value.length! > 0 && !allChecked.value\n})\n\n/** 全选*/\nconst handleCheckAll = (checked: boolean) => {\n treeRef.value?.checkAll(checked)\n\n dropdownRef.value?.updateDropdown()\n}\n\n/**选中 */\nconst handleCheck = (checked: (string | number)[], checkedData: Record<string, any>[]) => {\n markEvent()\n tags.value = checkedData\n emit('change', checkedData!)\n\n dropdownRef.value?.updateDropdown()\n}\n\n/**删除 */\nconst handleRemove = (index: number) => {\n markEvent()\n tags.value = tags.value.filter((_, i) => i !== index)\n model.value = model.value.filter((_, i) => i !== index)\n emit('change', tags.value)\n}\n\n/**清空 */\nconst handleClear = () => {\n markEvent()\n tags.value = []\n model.value = []\n emit('clear')\n}\n\nconst keyDicts = shallowRef(new Map<string | number, Record<string, any>>())\n\nwatch(\n treeData,\n (data) => {\n if (!data?.length) {\n keyDicts.value = new Map()\n } else {\n const newDict = new Map()\n data.forEach((item) => {\n dfs(\n item,\n (v) => {\n newDict.set(v[valueKey.value], v)\n },\n props.childrenKey\n )\n })\n keyDicts.value = newDict\n }\n },\n { immediate: true }\n)\n\nwatch(\n [keyDicts, model],\n ([keyDicts, model]) => {\n if (changedByEvent) return\n\n if (!keyDicts.size || !model?.length) {\n tags.value = []\n return\n }\n\n tags.value = model.filter((v) => keyDicts.has(v)).map((v) => keyDicts.get(v)!)\n },\n { immediate: true }\n)\n\nconst allExpanded = shallowRef(props.expandAll)\nfunction handleToggleExpandAll() {\n allExpanded.value = !allExpanded.value\n allExpanded.value ? treeRef.value?.expandAll() : treeRef.value?.collapseAll()\n}\n\nwatch(treeRef, (treeRef) => {\n if (!treeRef) {\n allExpanded.value = props.expandAll\n }\n})\n\nconst limit = () => {\n let { visibilityLimit } = props\n\n if (visibilityLimit < 0) {\n visibilityLimit = 0\n }\n if (disabled.value || readonly.value) {\n visibilityLimit = model.value?.length ?? 0\n }\n return visibilityLimit\n}\n\nconst visibleTags = computed(() => {\n if (tags.value.length > limit()) {\n return tags.value.slice(0, limit())\n }\n return tags.value\n})\n\nconst hiddenCount = computed(() => {\n if (tags.value.length > limit()) {\n return tags.value.length - limit()\n }\n return 0\n})\n\nconst dropdownVisible = shallowRef(false)\n\nconst inputRef = useTemplateRef<HTMLInputElement>('inputRef')\n\nconst handleDropdownVisible = (visible: boolean) => {\n dropdownVisible.value = visible\n if (!visible) {\n qs.value = ''\n }\n}\n\n/**\n * 拦截输入区域的点击(@click.stop):阻止 dropdown 的 trigger 开合切换,\n * 保持面板展开以不中断输入。\n */\nconst handleInputClick = () => {\n if (!dropdownVisible.value) dropdownRef.value?.open()\n}\n\nconst handleInputFocus = () => {\n if (!dropdownVisible.value) dropdownRef.value?.open()\n}\n</script>\n"],"mappings":"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"}
@@ -155,3 +155,10 @@
155
155
  .u-multi-tree-select__content-tree {
156
156
  max-height: 240px;
157
157
  }
158
+ .u-multi-tree-select__loading {
159
+ display: flex;
160
+ justify-content: center;
161
+ align-items: center;
162
+ flex-wrap: nowrap;
163
+ min-height: 240px;
164
+ }
@@ -3,6 +3,7 @@ import _sfc_main$2 from "../empty/empty.js";
3
3
  import _sfc_main$3 from "../scroll/scroll.js";
4
4
  import _sfc_main$4 from "../dropdown/dropdown.js";
5
5
  import input_default from "../input/input.js";
6
+ import _sfc_main$5 from "../loading/loading.js";
6
7
  import { useKeyboard } from "./use-keyboard.js";
7
8
  import { useOptions } from "./use-options.js";
8
9
  import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createSlots, createTextVNode, createVNode, defineComponent, mergeProps, nextTick, normalizeClass, normalizeStyle, openBlock, renderList, renderSlot, shallowRef, toDisplayString, unref, useTemplateRef, watch, withCtx, withModifiers } from "vue";
@@ -85,7 +86,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
85
86
  const filterable = computed(() => {
86
87
  return props.filterable || typeof props.options === "function";
87
88
  });
88
- const { queryString, options, allOptions, temOptionsToCreatedOptions, clearCreatedOptions } = useOptions({ props });
89
+ const { queryString, loading, options, allOptions, temOptionsToCreatedOptions, clearCreatedOptions } = useOptions({ props });
89
90
  const dropdownVisible = shallowRef(false);
90
91
  /** 选中临时选项后待转正标记:面板关闭后再落定,避免关闭动画期间列表恢复全量导致面板变长 */
91
92
  let pendingTempPromote = false;
@@ -313,8 +314,11 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
313
314
  "onKeydown",
314
315
  "native-readonly"
315
316
  ])]),
316
- content: withCtx(() => [createCommentVNode("\n 单选列表。虚拟化启用时,内容容器 height 由 useVirtualizer 命令式写入;\n 此处 content-style 仅承担 grid 相关样式,避免 height 变化触发模板重渲染。\n "), unref(options).length ? (openBlock(), createBlock(unref(_sfc_main$3), {
317
+ content: withCtx(() => [createCommentVNode("\n 单选列表。虚拟化启用时,内容容器 height 由 useVirtualizer 命令式写入;\n 此处 content-style 仅承担 grid 相关样式,避免 height 变化触发模板重渲染。\n "), unref(loading) ? (openBlock(), createElementBlock("div", {
317
318
  key: 0,
319
+ class: normalizeClass(unref(cls).e("loading"))
320
+ }, [createVNode(unref(_sfc_main$5), { type: "dual-ring" })], 2)) : unref(options).length ? (openBlock(), createBlock(unref(_sfc_main$3), {
321
+ key: 1,
318
322
  tag: "ul",
319
323
  class: normalizeClass(unref(cls).e("options")),
320
324
  ref_key: "scrollRef",
@@ -360,7 +364,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
360
364
  "content-class",
361
365
  "content-style"
362
366
  ])) : (openBlock(), createElementBlock("div", {
363
- key: 1,
367
+ key: 2,
364
368
  class: normalizeClass(unref(cls).e("empty"))
365
369
  }, [createVNode(unref(_sfc_main$2))], 2))]),
366
370
  _: 3
@@ -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 @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, useUserAction, 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)\n/** 内部展示文案,仅由选项推导;通过 update:text 单向通知父级 */\nconst label = shallowRef<string>()\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, allOptions, temOptionsToCreatedOptions, clearCreatedOptions } =\n useOptions({ props })\n\nconst dropdownVisible = shallowRef(false)\n\n/** 选中临时选项后待转正标记:面板关闭后再落定,避免关闭动画期间列表恢复全量导致面板变长 */\nlet pendingTempPromote = 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\nconst { userAction, isUserActive } = useUserAction()\n\n/** 更新内部文案;值变化时单向 emit update:text */\nfunction setLabel(next?: string) {\n if (label.value === next) return\n label.value = next\n emit('update:text', next)\n}\n\n/** 按 modelValue 与完整选项列表同步高亮索引、选中项与显示标签(外部回显用,O(n)) */\nfunction syncSelected(modelValue: any, sourceOptions: Record<string, any>[] | undefined) {\n // 查询输入期间高亮第一项(创建模式下通常是临时项,便于回车创建)\n if (queryString.value) {\n currentIndex.value = options.value.length ? 0 : -1\n return\n }\n\n if (!sourceOptions?.length) return\n\n if (modelValue !== undefined && modelValue !== null && modelValue !== '') {\n currentIndex.value = sourceOptions.findIndex(\n (option) => o(option).get(valueKey.value) === modelValue\n )\n selected.value = currentIndex.value >= 0 ? sourceOptions[currentIndex.value] : undefined\n setLabel(selected.value ? o(selected.value).get(labelKey.value) : undefined)\n } else {\n currentIndex.value = -1\n selected.value = undefined\n setLabel(undefined)\n }\n}\n\n// 回显:以 allOptions 匹配,避免过滤列表缺项时清掉已选文案;用户动作期内跳过\nwatch(\n [() => props.modelValue, allOptions],\n ([modelValue]) => {\n if (isUserActive()) return\n syncSelected(modelValue, allOptions.value)\n },\n { immediate: true }\n)\n\n// 过滤时单独同步高亮(allOptions 不变,不会触发上方回显 watch)\nwatch([queryString, options], () => {\n if (!queryString.value) return\n currentIndex.value = options.value.length ? 0 : -1\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\n/** 落定待转正的临时选项(面板 DOM 已卸载,列表恢复全量用户不可见) */\nfunction promotePendingTemp() {\n if (!pendingTempPromote) return\n pendingTempPromote = false\n temOptionsToCreatedOptions()\n}\n\nwatch(dropdownVisible, (visible) => {\n if (visible) {\n // 关闭动画期间被重新打开时补一次转正,保证已选临时项落到完整列表\n promotePendingTemp()\n // 面板展开后进入查询态并聚焦输入框,可以立即输入查询\n if (filterable.value) {\n queryString.value = ''\n querying.value = true\n nextTick(() => inputRef.value?.el?.focus())\n }\n } else {\n // 面板完全关闭(动画结束、DOM 卸载)后才清空查询串并转正,关闭动画期间列表保持过滤态\n querying.value = false\n queryString.value = ''\n promotePendingTemp()\n }\n})\n\n/** 单选:用户动作内 O(1) 写入值与文案,跳过 modelValue 回显的 O(n) 查找 */\nconst handleSelect = userAction((option: Record<string, any>, _index: number) => {\n const value = option?.[valueKey.value]\n selected.value = option\n\n emit('update:modelValue', value)\n setLabel(option?.[labelKey.value])\n emit('change', option)\n if (option.__isTemp) {\n // 转正延迟到面板关闭后:动画期间保持过滤列表,避免列表瞬间恢复全量导致面板变长闪烁\n pendingTempPromote = true\n }\n // 立即退出查询态,输入框同步恢复显示选中标签;查询串保留至面板关闭,避免关闭动画期间列表恢复全量\n querying.value = false\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\n/** 清除选项 */\nconst handleClear = userAction(() => {\n selected.value = undefined\n currentIndex.value = -1\n setLabel(undefined)\n clearCreatedOptions()\n emit('update:modelValue', undefined)\n emit('change', undefined)\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 @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 <div v-if=\"loading\" :class=\"cls.e('loading')\">\n <ULoading type=\"dual-ring\" />\n </div>\n <u-scroll\n v-else-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, useUserAction, 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 { ULoading } from '../loading'\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)\n/** 内部展示文案,仅由选项推导;通过 update:text 单向通知父级 */\nconst label = shallowRef<string>()\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 {\n queryString,\n loading,\n options,\n allOptions,\n temOptionsToCreatedOptions,\n clearCreatedOptions\n} = useOptions({ props })\n\nconst dropdownVisible = shallowRef(false)\n\n/** 选中临时选项后待转正标记:面板关闭后再落定,避免关闭动画期间列表恢复全量导致面板变长 */\nlet pendingTempPromote = 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\nconst { userAction, isUserActive } = useUserAction()\n\n/** 更新内部文案;值变化时单向 emit update:text */\nfunction setLabel(next?: string) {\n if (label.value === next) return\n label.value = next\n emit('update:text', next)\n}\n\n/** 按 modelValue 与完整选项列表同步高亮索引、选中项与显示标签(外部回显用,O(n)) */\nfunction syncSelected(modelValue: any, sourceOptions: Record<string, any>[] | undefined) {\n // 查询输入期间高亮第一项(创建模式下通常是临时项,便于回车创建)\n if (queryString.value) {\n currentIndex.value = options.value.length ? 0 : -1\n return\n }\n\n if (!sourceOptions?.length) return\n\n if (modelValue !== undefined && modelValue !== null && modelValue !== '') {\n currentIndex.value = sourceOptions.findIndex(\n (option) => o(option).get(valueKey.value) === modelValue\n )\n selected.value = currentIndex.value >= 0 ? sourceOptions[currentIndex.value] : undefined\n setLabel(selected.value ? o(selected.value).get(labelKey.value) : undefined)\n } else {\n currentIndex.value = -1\n selected.value = undefined\n setLabel(undefined)\n }\n}\n\n// 回显:以 allOptions 匹配,避免过滤列表缺项时清掉已选文案;用户动作期内跳过\nwatch(\n [() => props.modelValue, allOptions],\n ([modelValue]) => {\n if (isUserActive()) return\n syncSelected(modelValue, allOptions.value)\n },\n { immediate: true }\n)\n\n// 过滤时单独同步高亮(allOptions 不变,不会触发上方回显 watch)\nwatch([queryString, options], () => {\n if (!queryString.value) return\n currentIndex.value = options.value.length ? 0 : -1\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\n/** 落定待转正的临时选项(面板 DOM 已卸载,列表恢复全量用户不可见) */\nfunction promotePendingTemp() {\n if (!pendingTempPromote) return\n pendingTempPromote = false\n temOptionsToCreatedOptions()\n}\n\nwatch(dropdownVisible, (visible) => {\n if (visible) {\n // 关闭动画期间被重新打开时补一次转正,保证已选临时项落到完整列表\n promotePendingTemp()\n // 面板展开后进入查询态并聚焦输入框,可以立即输入查询\n if (filterable.value) {\n queryString.value = ''\n querying.value = true\n nextTick(() => inputRef.value?.el?.focus())\n }\n } else {\n // 面板完全关闭(动画结束、DOM 卸载)后才清空查询串并转正,关闭动画期间列表保持过滤态\n querying.value = false\n queryString.value = ''\n promotePendingTemp()\n }\n})\n\n/** 单选:用户动作内 O(1) 写入值与文案,跳过 modelValue 回显的 O(n) 查找 */\nconst handleSelect = userAction((option: Record<string, any>, _index: number) => {\n const value = option?.[valueKey.value]\n selected.value = option\n\n emit('update:modelValue', value)\n setLabel(option?.[labelKey.value])\n emit('change', option)\n if (option.__isTemp) {\n // 转正延迟到面板关闭后:动画期间保持过滤列表,避免列表瞬间恢复全量导致面板变长闪烁\n pendingTempPromote = true\n }\n // 立即退出查询态,输入框同步恢复显示选中标签;查询串保留至面板关闭,避免关闭动画期间列表恢复全量\n querying.value = false\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\n/** 清除选项 */\nconst handleClear = userAction(() => {\n selected.value = undefined\n currentIndex.value = -1\n setLabel(undefined)\n clearCreatedOptions()\n emit('update:modelValue', undefined)\n emit('change', undefined)\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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"}
@@ -119,3 +119,10 @@
119
119
  text-align: center;
120
120
  padding: var(--u-gap-small) 0;
121
121
  }
122
+ .u-select__loading {
123
+ display: flex;
124
+ justify-content: center;
125
+ align-items: center;
126
+ flex-wrap: nowrap;
127
+ min-height: 240px;
128
+ }
@@ -6,6 +6,9 @@ function useOptions(o$1) {
6
6
  const { props } = o$1;
7
7
  /** 筛选 */
8
8
  const queryString = shallowRef("");
9
+ const loading = shallowRef(false);
10
+ /** 远程请求序号;响应返回时序号不一致说明已有更新的请求,结果作废 */
11
+ let remoteSeq = 0;
9
12
  const remoteOptions = shallowRef([]);
10
13
  const filteredOptions = shallowRef([]);
11
14
  const tempOptions = shallowRef([]);
@@ -90,9 +93,16 @@ function useOptions(o$1) {
90
93
  applyLocalFilter(qs, propsOptions);
91
94
  }, 200);
92
95
  const debouncedRemoteFilter = debounce(async (qs, propsOptions) => {
93
- const options = await propsOptions(queryString.value);
94
- remoteOptions.value = options;
95
- setTempOption(queryString.value, options);
96
+ const seq = ++remoteSeq;
97
+ loading.value = true;
98
+ try {
99
+ const options = await propsOptions(qs);
100
+ if (seq !== remoteSeq || qs !== queryString.value) return;
101
+ remoteOptions.value = options ?? [];
102
+ setTempOption(qs, remoteOptions.value);
103
+ } finally {
104
+ if (seq === remoteSeq) loading.value = false;
105
+ }
96
106
  }, 200);
97
107
  watch([
98
108
  queryString,
@@ -111,6 +121,7 @@ function useOptions(o$1) {
111
121
  }, { immediate: true });
112
122
  return {
113
123
  queryString,
124
+ loading,
114
125
  options,
115
126
  allOptions,
116
127
  temOptionsToCreatedOptions,
@@ -1 +1 @@
1
- {"version":3,"file":"use-options.js","names":["o","chainObj"],"sources":["../../../src/components/select/use-options.ts"],"sourcesContent":["import { debounce, o as chainObj } from '@cat-kit/core'\nimport { fieldKey } from '@veltra/utils'\nimport { computed, shallowRef, watch, type ShallowRef, type ComputedRef } from 'vue'\n\nimport type { SelectProps } from '../../types'\n\ninterface Options {\n props: SelectProps\n}\n\ninterface UseOptionsReturned {\n /** 查询字符串 */\n queryString: ShallowRef<string>\n /** 选项 */\n options: ComputedRef<Record<string, any>[]>\n /** 所有选项 */\n allOptions: ComputedRef<Record<string, any>[]>\n /** 将临时选项转换为创建的选项 */\n temOptionsToCreatedOptions: () => void\n /** 清空用户创建的选项 */\n clearCreatedOptions: () => void\n}\n\n// 允许创建的实现\n// 1. 在过滤时将选项加入待选列表中\n// 2. 如果选择了该临时选项,则取消该选项的临时标记并并入已创建列表\n// 3. 多次创建累计保留,已创建项参与后续过滤匹配\n\nexport function useOptions(o: Options): UseOptionsReturned {\n const { props } = o\n\n /** 筛选 */\n const queryString = shallowRef('')\n\n const remoteOptions = shallowRef<Record<string, any>[]>([])\n const filteredOptions = shallowRef<Record<string, any>[]>([])\n // 临时选项\n const tempOptions = shallowRef<({ __isTemp: true } & Record<string, any>)[]>([])\n // 创建的选项\n const createdOptions = shallowRef<({ __isTemp: false } & Record<string, any>)[]>([])\n\n function getSourceOptions(propsOptions: SelectProps['options']) {\n if (typeof propsOptions === 'function') return remoteOptions.value\n return [...createdOptions.value, ...(propsOptions ?? [])]\n }\n\n /** 无查询时同步展示完整列表(含已创建项),避免选择后等高亮索引错位 */\n function resetToSourceOptions() {\n const sourceOptions = getSourceOptions(props.options)\n if (typeof props.options === 'function') {\n setTempOption('', remoteOptions.value)\n return\n }\n filteredOptions.value = sourceOptions\n tempOptions.value = []\n }\n\n function temOptionsToCreatedOptions() {\n if (!tempOptions.value.length) return\n\n createdOptions.value = [\n ...createdOptions.value,\n ...tempOptions.value.map((item) => {\n return { ...item, __isTemp: false as const }\n })\n ]\n // 立即清掉临时项并回到完整列表,不等待过滤防抖\n resetToSourceOptions()\n }\n\n function clearCreatedOptions() {\n createdOptions.value = []\n resetToSourceOptions()\n }\n\n const setTempOption = (qs: string, options?: Record<string, any>[]) => {\n if (!qs?.trim() || !props.creatable) {\n tempOptions.value = []\n return\n }\n const labelKey = fieldKey(props.labelKey, 'label')\n const valueKey = fieldKey(props.valueKey, 'value')\n const exactMatch = options\n ? options.some((option) => {\n return chainObj(option).get(labelKey) === qs\n })\n : false\n\n tempOptions.value = exactMatch ? [] : [{ [labelKey]: qs, [valueKey]: qs, __isTemp: true }]\n }\n\n const allOptions = computed(() => {\n const { options } = props\n if (typeof options === 'function') return remoteOptions.value\n return [...createdOptions.value, ...(options ?? [])]\n })\n\n /**\n * 展示的选项\n * @description 已创建选项并入过滤数据源参与匹配;临时选项(待创建)固定置顶\n */\n const options = computed(() => {\n const { options } = props\n\n if (typeof options === 'function') return remoteOptions.value\n\n return [...tempOptions.value, ...filteredOptions.value]\n })\n\n const applyLocalFilter = (qs: string, propsOptions: Record<string, any>[] | undefined) => {\n const labelKey = fieldKey(props.labelKey, 'label')\n const sourceOptions = [...createdOptions.value, ...(propsOptions ?? [])]\n\n if (!qs) {\n filteredOptions.value = sourceOptions\n setTempOption(qs, filteredOptions.value)\n return\n }\n\n const _filteredOptions = sourceOptions.filter((item) => {\n return chainObj(item).get(labelKey)?.includes(qs) ?? false\n })\n\n setTempOption(qs, _filteredOptions)\n filteredOptions.value = _filteredOptions\n }\n\n // 仅对「有查询词」的本地过滤做防抖;清空查询同步生效,避免选中后列表延迟重建\n const debouncedLocalFilter = debounce(\n (qs: string, propsOptions: Record<string, any>[] | undefined) => {\n // 防抖回调可能晚于清空查询到达,以当前 queryString 为准\n if (!queryString.value) {\n applyLocalFilter('', propsOptions)\n return\n }\n applyLocalFilter(qs, propsOptions)\n },\n 200\n )\n\n const debouncedRemoteFilter = debounce(async (qs: string, propsOptions: Function) => {\n const options = await propsOptions(queryString.value)\n remoteOptions.value = options\n setTempOption(queryString.value, options)\n }, 200)\n\n watch(\n [queryString, () => props.options, createdOptions],\n ([qs, propsOptions]) => {\n if (typeof propsOptions === 'function') {\n debouncedRemoteFilter(qs, propsOptions)\n return\n }\n\n if (!qs) {\n applyLocalFilter('', propsOptions)\n return\n }\n\n debouncedLocalFilter(qs, propsOptions)\n },\n { immediate: true }\n )\n\n return { queryString, options, allOptions, temOptionsToCreatedOptions, clearCreatedOptions }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-options.js","names":["o","chainObj"],"sources":["../../../src/components/select/use-options.ts"],"sourcesContent":["import { debounce, o as chainObj } from '@cat-kit/core'\nimport { fieldKey } from '@veltra/utils'\nimport { computed, shallowRef, watch, type ShallowRef, type ComputedRef } from 'vue'\n\nimport type { SelectProps } from '../../types'\n\ninterface Options {\n props: SelectProps\n}\n\ninterface UseOptionsReturned {\n /** 查询字符串 */\n queryString: ShallowRef<string>\n /** 远程搜索加载中 */\n loading: ShallowRef<boolean>\n /** 选项 */\n options: ComputedRef<Record<string, any>[]>\n /** 所有选项 */\n allOptions: ComputedRef<Record<string, any>[]>\n /** 将临时选项转换为创建的选项 */\n temOptionsToCreatedOptions: () => void\n /** 清空用户创建的选项 */\n clearCreatedOptions: () => void\n}\n\n// 允许创建的实现\n// 1. 在过滤时将选项加入待选列表中\n// 2. 如果选择了该临时选项,则取消该选项的临时标记并并入已创建列表\n// 3. 多次创建累计保留,已创建项参与后续过滤匹配\n\nexport function useOptions(o: Options): UseOptionsReturned {\n const { props } = o\n\n /** 筛选 */\n const queryString = shallowRef('')\n\n const loading = shallowRef(false)\n /** 远程请求序号;响应返回时序号不一致说明已有更新的请求,结果作废 */\n let remoteSeq = 0\n\n const remoteOptions = shallowRef<Record<string, any>[]>([])\n const filteredOptions = shallowRef<Record<string, any>[]>([])\n // 临时选项\n const tempOptions = shallowRef<({ __isTemp: true } & Record<string, any>)[]>([])\n // 创建的选项\n const createdOptions = shallowRef<({ __isTemp: false } & Record<string, any>)[]>([])\n\n function getSourceOptions(propsOptions: SelectProps['options']) {\n if (typeof propsOptions === 'function') return remoteOptions.value\n return [...createdOptions.value, ...(propsOptions ?? [])]\n }\n\n /** 无查询时同步展示完整列表(含已创建项),避免选择后等高亮索引错位 */\n function resetToSourceOptions() {\n const sourceOptions = getSourceOptions(props.options)\n if (typeof props.options === 'function') {\n setTempOption('', remoteOptions.value)\n return\n }\n filteredOptions.value = sourceOptions\n tempOptions.value = []\n }\n\n function temOptionsToCreatedOptions() {\n if (!tempOptions.value.length) return\n\n createdOptions.value = [\n ...createdOptions.value,\n ...tempOptions.value.map((item) => {\n return { ...item, __isTemp: false as const }\n })\n ]\n // 立即清掉临时项并回到完整列表,不等待过滤防抖\n resetToSourceOptions()\n }\n\n function clearCreatedOptions() {\n createdOptions.value = []\n resetToSourceOptions()\n }\n\n const setTempOption = (qs: string, options?: Record<string, any>[]) => {\n if (!qs?.trim() || !props.creatable) {\n tempOptions.value = []\n return\n }\n const labelKey = fieldKey(props.labelKey, 'label')\n const valueKey = fieldKey(props.valueKey, 'value')\n const exactMatch = options\n ? options.some((option) => {\n return chainObj(option).get(labelKey) === qs\n })\n : false\n\n tempOptions.value = exactMatch ? [] : [{ [labelKey]: qs, [valueKey]: qs, __isTemp: true }]\n }\n\n const allOptions = computed(() => {\n const { options } = props\n if (typeof options === 'function') return remoteOptions.value\n return [...createdOptions.value, ...(options ?? [])]\n })\n\n /**\n * 展示的选项\n * @description 已创建选项并入过滤数据源参与匹配;临时选项(待创建)固定置顶\n */\n const options = computed(() => {\n const { options } = props\n\n if (typeof options === 'function') return remoteOptions.value\n\n return [...tempOptions.value, ...filteredOptions.value]\n })\n\n const applyLocalFilter = (qs: string, propsOptions: Record<string, any>[] | undefined) => {\n const labelKey = fieldKey(props.labelKey, 'label')\n const sourceOptions = [...createdOptions.value, ...(propsOptions ?? [])]\n\n if (!qs) {\n filteredOptions.value = sourceOptions\n setTempOption(qs, filteredOptions.value)\n return\n }\n\n const _filteredOptions = sourceOptions.filter((item) => {\n return chainObj(item).get(labelKey)?.includes(qs) ?? false\n })\n\n setTempOption(qs, _filteredOptions)\n filteredOptions.value = _filteredOptions\n }\n\n // 仅对「有查询词」的本地过滤做防抖;清空查询同步生效,避免选中后列表延迟重建\n const debouncedLocalFilter = debounce(\n (qs: string, propsOptions: Record<string, any>[] | undefined) => {\n // 防抖回调可能晚于清空查询到达,以当前 queryString 为准\n if (!queryString.value) {\n applyLocalFilter('', propsOptions)\n return\n }\n applyLocalFilter(qs, propsOptions)\n },\n 200\n )\n\n const debouncedRemoteFilter = debounce(async (qs: string, propsOptions: Function) => {\n const seq = ++remoteSeq\n loading.value = true\n try {\n const options = await propsOptions(qs)\n // 响应到达时查询词可能已变化或已有更新的请求,过期结果直接丢弃\n if (seq !== remoteSeq || qs !== queryString.value) return\n remoteOptions.value = options ?? []\n setTempOption(qs, remoteOptions.value)\n } finally {\n if (seq === remoteSeq) loading.value = false\n }\n }, 200)\n\n watch(\n [queryString, () => props.options, createdOptions],\n ([qs, propsOptions]) => {\n if (typeof propsOptions === 'function') {\n debouncedRemoteFilter(qs, propsOptions)\n return\n }\n\n if (!qs) {\n applyLocalFilter('', propsOptions)\n return\n }\n\n debouncedLocalFilter(qs, propsOptions)\n },\n { immediate: true }\n )\n\n return {\n queryString,\n loading,\n options,\n allOptions,\n temOptionsToCreatedOptions,\n clearCreatedOptions\n }\n}\n"],"mappings":"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"}
@@ -50,6 +50,13 @@
50
50
  .u-tree-select__content-tree {
51
51
  max-height: 240px;
52
52
  }
53
+ .u-tree-select__loading {
54
+ display: flex;
55
+ justify-content: center;
56
+ align-items: center;
57
+ flex-wrap: nowrap;
58
+ min-height: 240px;
59
+ }
53
60
  .u-tree-select__panel--small .u-tree-select__content-tree {
54
61
  padding: var(--u-gap-small);
55
62
  }