@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.
- package/dist/components/batch-edit/batch-edit-form.js +40 -26
- package/dist/components/batch-edit/batch-edit-form.js.map +1 -1
- package/dist/components/batch-edit/batch-edit-list.js +17 -5
- package/dist/components/batch-edit/batch-edit-list.js.map +1 -1
- package/dist/components/batch-edit/di.js.map +1 -1
- package/dist/components/batch-edit/use-handlers.js +3 -3
- package/dist/components/batch-edit/use-handlers.js.map +1 -1
- package/dist/components/batch-edit/use-shortcut-key.js +4 -2
- package/dist/components/batch-edit/use-shortcut-key.js.map +1 -1
- package/dist/components/card/style2.css +0 -2
- package/dist/components/dialog/style2.css +1 -1
- package/dist/components/dropdown/dropdown.js +5 -3
- package/dist/components/dropdown/dropdown.js.map +1 -1
- package/dist/components/expression-editor/components/path-preview.js +3 -6
- package/dist/components/expression-editor/components/path-preview.js.map +1 -1
- package/dist/components/expression-editor/components/variable-picker.js +49 -12
- package/dist/components/expression-editor/components/variable-picker.js.map +1 -1
- package/dist/components/expression-editor/core/chip.js +1 -15
- package/dist/components/expression-editor/core/chip.js.map +1 -1
- package/dist/components/expression-editor/core/editor.js +20 -18
- package/dist/components/expression-editor/core/editor.js.map +1 -1
- package/dist/components/expression-editor/expression-editor.js +53 -22
- package/dist/components/expression-editor/expression-editor.js.map +1 -1
- package/dist/components/expression-editor/style2.css +135 -96
- package/dist/components/form/form.js.map +1 -1
- package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js +0 -1
- package/dist/components/form/form.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/form/style2.css +8 -3
- package/dist/components/index.d.ts +26 -26
- package/dist/components/index.js +52 -52
- package/dist/components/multi-select/multi-select.js +16 -12
- package/dist/components/multi-select/multi-select.js.map +1 -1
- package/dist/components/multi-select/style2.css +7 -0
- package/dist/components/multi-tree-select/multi-tree-select.js +82 -56
- package/dist/components/multi-tree-select/multi-tree-select.js.map +1 -1
- package/dist/components/multi-tree-select/style2.css +7 -0
- package/dist/components/select/select.js +7 -3
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/select/style2.css +7 -0
- package/dist/components/select/use-options.js +14 -3
- package/dist/components/select/use-options.js.map +1 -1
- package/dist/components/tree-select/style2.css +7 -0
- package/dist/components/tree-select/tree-select.js +49 -17
- package/dist/components/tree-select/tree-select.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/types/dropdown.d.ts +10 -0
- package/dist/types/multi-tree-select.d.ts +7 -1
- package/dist/types/tree-select.d.ts +7 -1
- package/package.json +1 -1
- package/src/components/batch-edit/__test__/batch-edit.test.ts +119 -7
- package/src/components/batch-edit/batch-edit-form.vue +21 -2
- package/src/components/batch-edit/batch-edit-list.vue +18 -1
- package/src/components/batch-edit/di.ts +1 -1
- package/src/components/batch-edit/use-handlers.ts +6 -5
- package/src/components/batch-edit/use-shortcut-key.ts +5 -2
- package/src/components/card/style.scss +0 -2
- package/src/components/dialog/style.scss +2 -1
- package/src/components/dropdown/dropdown.vue +9 -4
- package/src/components/expression-editor/__test__/chip.test.ts +3 -14
- package/src/components/expression-editor/components/path-preview.vue +1 -2
- package/src/components/expression-editor/components/variable-picker.vue +54 -13
- package/src/components/expression-editor/core/chip.ts +3 -29
- package/src/components/expression-editor/core/editor.ts +28 -21
- package/src/components/expression-editor/expression-editor.vue +47 -21
- package/src/components/expression-editor/style.scss +167 -111
- package/src/components/form/form.vue +0 -1
- package/src/components/form/style.scss +11 -4
- package/src/components/multi-select/multi-select.vue +11 -2
- package/src/components/multi-select/style.scss +6 -0
- package/src/components/multi-tree-select/multi-tree-select.vue +58 -9
- package/src/components/multi-tree-select/style.scss +6 -0
- package/src/components/select/__test__/select.test.ts +78 -0
- package/src/components/select/select.vue +13 -3
- package/src/components/select/style.scss +6 -0
- package/src/components/select/use-options.ts +25 -4
- package/src/components/tree-select/__test__/tree-select.test.ts +116 -0
- package/src/components/tree-select/style.scss +6 -0
- package/src/components/tree-select/tree-select.vue +63 -13
- package/src/types/dropdown.ts +10 -0
- package/src/types/multi-tree-select.ts +12 -1
- package/src/types/tree-select.ts +12 -1
|
@@ -5,48 +5,71 @@
|
|
|
5
5
|
$root-name: expression-editor;
|
|
6
6
|
|
|
7
7
|
@include m.b($root-name) {
|
|
8
|
+
// 根节点只做布局(提示行在视觉盒之外),视觉盒由 __shell 承担
|
|
8
9
|
position: relative;
|
|
9
|
-
background-color: fn.use-var(bg-color, top);
|
|
10
|
-
border-width: fn.use-var(border, width);
|
|
11
|
-
border-style: fn.use-var(border, style);
|
|
12
|
-
border-color: fn.use-var(border, muted-color);
|
|
13
|
-
border-radius: fn.use-var(radius, default);
|
|
14
10
|
width: 100%;
|
|
15
11
|
|
|
16
|
-
@include m.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
@include m.e(shell) {
|
|
13
|
+
background-color: fn.use-var(bg-color, top);
|
|
14
|
+
border-width: fn.use-var(border, width);
|
|
15
|
+
border-style: fn.use-var(border, style);
|
|
16
|
+
border-color: fn.use-var(border, muted-color);
|
|
17
|
+
border-radius: fn.use-var(radius, default);
|
|
18
|
+
transition:
|
|
19
|
+
border-color fn.use-var(transition, fast) fn.use-var(transition, ease),
|
|
20
|
+
box-shadow fn.use-var(transition, fast) fn.use-var(transition, ease);
|
|
21
|
+
|
|
22
|
+
@include m.is-not(disabled, readonly) {
|
|
23
|
+
&:hover {
|
|
24
|
+
border-color: fn.use-var(color, primary);
|
|
25
|
+
}
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
&:focus-within {
|
|
28
|
+
border-color: fn.use-var(color, primary);
|
|
29
|
+
box-shadow: fn.use-var(focus-ring);
|
|
30
|
+
outline: none;
|
|
31
|
+
}
|
|
24
32
|
}
|
|
25
|
-
}
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
@include m.is('disabled') {
|
|
35
|
+
background-color: fn.use-var(color, disabled);
|
|
36
|
+
cursor: not-allowed;
|
|
37
|
+
border-color: fn.use-var(border, muted-color);
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
&:hover {
|
|
40
|
+
border-color: fn.use-var(border, muted-color);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@include m.is('readonly') {
|
|
45
|
+
background-color: fn.use-var(bg-color, middle);
|
|
46
|
+
cursor: default;
|
|
33
47
|
border-color: fn.use-var(border, muted-color);
|
|
48
|
+
user-select: text;
|
|
49
|
+
|
|
50
|
+
&:hover {
|
|
51
|
+
border-color: fn.use-var(border, muted-color);
|
|
52
|
+
}
|
|
34
53
|
}
|
|
35
|
-
}
|
|
36
54
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
border-color: fn.use-var(border, muted-color);
|
|
41
|
-
user-select: text;
|
|
55
|
+
@include m.size using ($size) {
|
|
56
|
+
min-height: fn.use-var(form-component-height, $size);
|
|
57
|
+
padding: fn.use-var(gap, $size);
|
|
42
58
|
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
@include m.bem($root-name, placeholder) {
|
|
60
|
+
font-size: fn.use-var(font-size-main, $size);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@include m.bem($root-name, container) {
|
|
64
|
+
font-size: fn.use-var(font-size-main, $size);
|
|
65
|
+
}
|
|
45
66
|
}
|
|
46
67
|
}
|
|
47
68
|
|
|
48
69
|
@include m.e(placeholder) {
|
|
49
70
|
position: absolute;
|
|
71
|
+
top: 0;
|
|
72
|
+
left: 0;
|
|
50
73
|
color: fn.use-var(text-color, placeholder);
|
|
51
74
|
pointer-events: none;
|
|
52
75
|
user-select: none;
|
|
@@ -54,6 +77,43 @@ $root-name: expression-editor;
|
|
|
54
77
|
font-weight: 400;
|
|
55
78
|
}
|
|
56
79
|
|
|
80
|
+
// 提示 + 输入区的定位层:placeholder 相对它对齐,hint 出现时不至于错位
|
|
81
|
+
@include m.e(body) {
|
|
82
|
+
position: relative;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 编辑器上方的 `@` 使用提示(空内容时显示),位于边框盒之外
|
|
86
|
+
@include m.e(hint) {
|
|
87
|
+
font-size: fn.use-var(font-size-main, small);
|
|
88
|
+
color: fn.use-var(text-color, placeholder);
|
|
89
|
+
user-select: none;
|
|
90
|
+
margin-bottom: 4px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// mention 面板定位锚点:跟随 `@` 字符,零尺寸不参与绘制
|
|
94
|
+
@include m.e(mention-anchor) {
|
|
95
|
+
position: absolute;
|
|
96
|
+
width: 0;
|
|
97
|
+
height: 0;
|
|
98
|
+
pointer-events: none;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 键位提示(编辑器 hint 与面板 footer 共用)
|
|
102
|
+
@include m.e(hint, picker-hint) {
|
|
103
|
+
kbd {
|
|
104
|
+
display: inline-block;
|
|
105
|
+
min-width: 18px;
|
|
106
|
+
padding: 1px 4px;
|
|
107
|
+
border: fn.use-var(border);
|
|
108
|
+
border-radius: fn.use-var(radius, small);
|
|
109
|
+
font-size: 11px;
|
|
110
|
+
line-height: 1.4;
|
|
111
|
+
text-align: center;
|
|
112
|
+
color: fn.use-var(text-color, second);
|
|
113
|
+
background-color: fn.use-var(bg-color, top);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
57
117
|
@include m.e(container) {
|
|
58
118
|
position: relative;
|
|
59
119
|
outline: none;
|
|
@@ -105,12 +165,6 @@ $root-name: expression-editor;
|
|
|
105
165
|
fn.use-var(color, primary, light-7)
|
|
106
166
|
);
|
|
107
167
|
box-shadow: 0 1px 4px fn.use-var(shadow, color);
|
|
108
|
-
|
|
109
|
-
#{fn.bem($root-name, chip-close)} {
|
|
110
|
-
opacity: 1;
|
|
111
|
-
max-width: 14px;
|
|
112
|
-
margin-left: 2px;
|
|
113
|
-
}
|
|
114
168
|
}
|
|
115
169
|
|
|
116
170
|
&.is-focused {
|
|
@@ -122,94 +176,93 @@ $root-name: expression-editor;
|
|
|
122
176
|
pointer-events: none;
|
|
123
177
|
}
|
|
124
178
|
|
|
125
|
-
@include m.e(chip-close) {
|
|
126
|
-
display: inline-flex;
|
|
127
|
-
align-items: center;
|
|
128
|
-
justify-content: center;
|
|
129
|
-
width: 14px;
|
|
130
|
-
max-width: 0;
|
|
131
|
-
margin-left: 0;
|
|
132
|
-
overflow: hidden;
|
|
133
|
-
font-size: 14px;
|
|
134
|
-
line-height: 1;
|
|
135
|
-
color: fn.use-var(color, primary);
|
|
136
|
-
opacity: 0;
|
|
137
|
-
transition:
|
|
138
|
-
opacity 0.12s ease,
|
|
139
|
-
max-width 0.12s ease,
|
|
140
|
-
margin-left 0.12s ease;
|
|
141
|
-
cursor: pointer;
|
|
142
|
-
|
|
143
|
-
&:hover {
|
|
144
|
-
transform: scale(1.1);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
179
|
// ─── 变量选择面板 ──────────────────────────────────────────
|
|
180
|
+
// 面板以 tip 为浮层载体,但按库内标准下拉面板形态渲染:
|
|
181
|
+
// 负 margin 抵消 tip 内容自带的 gap 内边距,由 picker 自绘边框卡片
|
|
149
182
|
@include m.e(picker) {
|
|
150
|
-
|
|
183
|
+
margin: calc(#{fn.use-var(gap, default)} * -1);
|
|
184
|
+
min-width: 240px;
|
|
185
|
+
display: flex;
|
|
186
|
+
flex-direction: column;
|
|
187
|
+
overflow: hidden;
|
|
151
188
|
background-color: fn.use-var(bg-color, top);
|
|
189
|
+
border: fn.use-var(border);
|
|
190
|
+
border-radius: fn.use-var(radius, default);
|
|
191
|
+
backdrop-filter: fn.use-var(bg-filter);
|
|
192
|
+
-webkit-backdrop-filter: fn.use-var(bg-filter);
|
|
152
193
|
}
|
|
153
194
|
|
|
154
195
|
@include m.e(picker-body) {
|
|
196
|
+
flex: 1 1 auto;
|
|
197
|
+
min-height: 0;
|
|
155
198
|
display: flex;
|
|
156
199
|
align-items: stretch;
|
|
157
|
-
min-height: 200px;
|
|
158
200
|
}
|
|
159
201
|
|
|
160
202
|
@include m.e(panel) {
|
|
161
203
|
flex: 1 1 auto;
|
|
162
204
|
min-width: 0;
|
|
163
205
|
max-height: 320px;
|
|
206
|
+
display: flex;
|
|
207
|
+
flex-direction: column;
|
|
164
208
|
}
|
|
165
209
|
|
|
166
210
|
@include m.e(panel-list) {
|
|
167
|
-
|
|
168
|
-
min-height:
|
|
211
|
+
flex: 1 1 auto;
|
|
212
|
+
min-height: 0;
|
|
169
213
|
}
|
|
170
214
|
|
|
171
215
|
@include m.e(panel-content) {
|
|
172
|
-
padding: 4px
|
|
216
|
+
padding: 4px;
|
|
173
217
|
}
|
|
174
218
|
|
|
175
219
|
@include m.e(item) {
|
|
176
|
-
padding: 6px 12px;
|
|
177
|
-
cursor: pointer;
|
|
178
|
-
user-select: none;
|
|
179
220
|
display: flex;
|
|
180
221
|
align-items: center;
|
|
181
222
|
justify-content: space-between;
|
|
182
|
-
gap:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
color 0.12s ease;
|
|
186
|
-
color: fn.use-var(text-color, main);
|
|
187
|
-
font-size: 13px;
|
|
188
|
-
line-height: 1.4;
|
|
223
|
+
gap: fn.use-var(gap, default);
|
|
224
|
+
height: fn.use-var(form-component-height, default);
|
|
225
|
+
padding: 0 fn.use-var(gap, default);
|
|
189
226
|
border-radius: fn.use-var(radius, small);
|
|
190
|
-
|
|
227
|
+
font-size: fn.use-var(font-size-main, default);
|
|
228
|
+
color: fn.use-var(text-color, main);
|
|
229
|
+
cursor: pointer;
|
|
230
|
+
user-select: none;
|
|
231
|
+
transition:
|
|
232
|
+
background-color 0.15s ease,
|
|
233
|
+
color 0.15s ease;
|
|
191
234
|
|
|
192
235
|
@include m.is(active) {
|
|
193
|
-
background-color: fn.use-var(color,
|
|
194
|
-
color: fn.use-var(text-color,
|
|
195
|
-
|
|
196
|
-
@include m.bem($root-name, item-expand) {
|
|
197
|
-
color: fn.use-var(text-color, white);
|
|
198
|
-
}
|
|
236
|
+
background-color: fn.use-var(bg-color, hover);
|
|
237
|
+
color: fn.use-var(text-color, title);
|
|
199
238
|
}
|
|
200
239
|
}
|
|
201
240
|
|
|
202
241
|
@include m.e(item-label) {
|
|
203
242
|
flex: 1;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
white-space: nowrap;
|
|
243
|
+
min-width: 0;
|
|
244
|
+
@include m.ellipsis;
|
|
207
245
|
}
|
|
208
246
|
|
|
209
247
|
@include m.e(item-expand) {
|
|
210
248
|
flex-shrink: 0;
|
|
211
249
|
color: fn.use-var(text-color, placeholder);
|
|
212
250
|
font-size: 12px;
|
|
251
|
+
// 扩大点击热区:u-icon 是固定 1em 盒(全局 border-box),padding 会把 svg 挤成 0 宽,
|
|
252
|
+
// 这里改回 content-box 让字形保持原尺寸,再用负 margin 抵消布局占位
|
|
253
|
+
box-sizing: content-box;
|
|
254
|
+
padding: 6px;
|
|
255
|
+
margin: -6px;
|
|
256
|
+
border-radius: fn.use-var(radius, small);
|
|
257
|
+
cursor: pointer;
|
|
258
|
+
transition:
|
|
259
|
+
color 0.15s ease,
|
|
260
|
+
background-color 0.15s ease;
|
|
261
|
+
|
|
262
|
+
&:hover {
|
|
263
|
+
color: fn.use-var(color, primary);
|
|
264
|
+
background-color: fn.use-var(bg-color, hover);
|
|
265
|
+
}
|
|
213
266
|
}
|
|
214
267
|
|
|
215
268
|
@include m.e(empty) {
|
|
@@ -218,30 +271,51 @@ $root-name: expression-editor;
|
|
|
218
271
|
text-align: center;
|
|
219
272
|
}
|
|
220
273
|
|
|
274
|
+
// 面板底部键盘操作提示
|
|
275
|
+
@include m.e(picker-hint) {
|
|
276
|
+
display: flex;
|
|
277
|
+
align-items: center;
|
|
278
|
+
flex-wrap: wrap;
|
|
279
|
+
gap: 4px 12px;
|
|
280
|
+
padding: 6px 10px;
|
|
281
|
+
border-top: fn.use-var(border);
|
|
282
|
+
font-size: fn.use-var(font-size-main, small);
|
|
283
|
+
color: fn.use-var(text-color, placeholder);
|
|
284
|
+
user-select: none;
|
|
285
|
+
|
|
286
|
+
span {
|
|
287
|
+
display: inline-flex;
|
|
288
|
+
align-items: center;
|
|
289
|
+
gap: 3px;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
221
293
|
// ─── 面包屑 ───────────────────────────────────────────────
|
|
222
294
|
@include m.e(breadcrumbs) {
|
|
223
295
|
display: flex;
|
|
224
296
|
align-items: center;
|
|
225
297
|
flex-wrap: wrap;
|
|
226
|
-
|
|
298
|
+
gap: 2px;
|
|
299
|
+
padding: 6px 8px;
|
|
300
|
+
background-color: fn.use-var(bg-color, middle);
|
|
227
301
|
border-bottom: fn.use-var(border);
|
|
228
|
-
font-size:
|
|
302
|
+
font-size: fn.use-var(font-size-main, small);
|
|
229
303
|
color: fn.use-var(text-color, second);
|
|
230
304
|
}
|
|
231
305
|
|
|
232
306
|
@include m.e(breadcrumb-item) {
|
|
233
307
|
display: inline-flex;
|
|
234
308
|
align-items: center;
|
|
235
|
-
gap:
|
|
309
|
+
gap: 2px;
|
|
236
310
|
}
|
|
237
311
|
|
|
238
312
|
@include m.e(breadcrumb-link) {
|
|
313
|
+
padding: 3px 6px;
|
|
314
|
+
border-radius: fn.use-var(radius, small);
|
|
239
315
|
cursor: pointer;
|
|
240
316
|
transition:
|
|
241
|
-
color 0.
|
|
242
|
-
background-color 0.
|
|
243
|
-
padding: 2px 6px;
|
|
244
|
-
border-radius: fn.use-var(radius, small);
|
|
317
|
+
color 0.15s ease,
|
|
318
|
+
background-color 0.15s ease;
|
|
245
319
|
|
|
246
320
|
&:hover {
|
|
247
321
|
color: fn.use-var(color, primary);
|
|
@@ -255,7 +329,7 @@ $root-name: expression-editor;
|
|
|
255
329
|
}
|
|
256
330
|
|
|
257
331
|
@include m.e(breadcrumb-separator) {
|
|
258
|
-
font-size:
|
|
332
|
+
font-size: 12px;
|
|
259
333
|
color: fn.use-var(text-color, placeholder);
|
|
260
334
|
}
|
|
261
335
|
|
|
@@ -265,7 +339,7 @@ $root-name: expression-editor;
|
|
|
265
339
|
border-left: fn.use-var(border);
|
|
266
340
|
padding: 8px 12px;
|
|
267
341
|
background-color: fn.use-var(bg-color, middle);
|
|
268
|
-
font-size:
|
|
342
|
+
font-size: fn.use-var(font-size-main, small);
|
|
269
343
|
}
|
|
270
344
|
|
|
271
345
|
@include m.e(path-preview-title) {
|
|
@@ -286,6 +360,8 @@ $root-name: expression-editor;
|
|
|
286
360
|
gap: 4px;
|
|
287
361
|
line-height: 1.6;
|
|
288
362
|
color: fn.use-var(text-color, main);
|
|
363
|
+
// 层级缩进由模板传入的 --depth 驱动
|
|
364
|
+
padding-left: calc(var(--depth, 0) * 12px);
|
|
289
365
|
|
|
290
366
|
@include m.is(active) {
|
|
291
367
|
color: fn.use-var(color, primary);
|
|
@@ -293,28 +369,8 @@ $root-name: expression-editor;
|
|
|
293
369
|
}
|
|
294
370
|
}
|
|
295
371
|
|
|
296
|
-
@include m.e(path-tree-branch) {
|
|
297
|
-
color: fn.use-var(text-color, placeholder);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
372
|
@include m.e(path-tree-label) {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
white-space: nowrap;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
@include m.size using ($size) {
|
|
307
|
-
min-height: fn.use-var(form-component-height, $size);
|
|
308
|
-
padding: fn.use-var(gap, $size);
|
|
309
|
-
|
|
310
|
-
@include m.bem($root-name, placeholder) {
|
|
311
|
-
top: fn.use-var(gap, $size);
|
|
312
|
-
left: fn.use-var(gap, $size);
|
|
313
|
-
font-size: fn.use-var(font-size-main, $size);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
@include m.bem($root-name, container) {
|
|
317
|
-
font-size: fn.use-var(font-size-main, $size);
|
|
318
|
-
}
|
|
373
|
+
min-width: 0;
|
|
374
|
+
@include m.ellipsis;
|
|
319
375
|
}
|
|
320
376
|
}
|
|
@@ -3,15 +3,22 @@
|
|
|
3
3
|
@use 'pkg:@veltra/styles/vars';
|
|
4
4
|
|
|
5
5
|
@include m.b(form) {
|
|
6
|
+
// gap 走 CSS 而非 u-grid 内联样式,只读态才能覆盖行距
|
|
7
|
+
gap: 0 12px;
|
|
8
|
+
|
|
9
|
+
// 只读态:label 退为次级文本,值收进浅色圆角条;行距拉开让各项独立成块,避免粘连
|
|
6
10
|
@include m.is(readonly) {
|
|
7
|
-
gap:
|
|
11
|
+
gap: 10px 12px;
|
|
8
12
|
|
|
9
13
|
@include m.bem(form-item, label) {
|
|
10
|
-
|
|
14
|
+
color: fn.use-var(text-color, second);
|
|
11
15
|
}
|
|
16
|
+
|
|
12
17
|
@include m.bem(form-item, content) {
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
padding: 4px 12px;
|
|
19
|
+
color: fn.use-var(text-color, main);
|
|
20
|
+
background-color: fn.use-var(bg-color, middle);
|
|
21
|
+
border-radius: fn.use-var(radius, default);
|
|
15
22
|
}
|
|
16
23
|
}
|
|
17
24
|
|
|
@@ -80,11 +80,14 @@
|
|
|
80
80
|
虚拟化启用时内容容器 height 由 useVirtualizer 命令式写入;
|
|
81
81
|
模板不再绑定 totalSize,避免尺寸变化引起重渲染。
|
|
82
82
|
-->
|
|
83
|
+
<div v-if="loading" :class="cls.e('loading')">
|
|
84
|
+
<ULoading type="dual-ring" />
|
|
85
|
+
</div>
|
|
83
86
|
<u-scroll
|
|
84
87
|
tag="ul"
|
|
85
88
|
:class="[cls.e('options')]"
|
|
86
89
|
ref="scrollRef"
|
|
87
|
-
v-if="options.length"
|
|
90
|
+
v-else-if="options.length"
|
|
88
91
|
:content-class="[cls.e('options-wrap'), bem.is('virtual', virtualEnabled)]"
|
|
89
92
|
>
|
|
90
93
|
<template v-if="virtualEnabled">
|
|
@@ -166,6 +169,7 @@ import { UCheckbox } from '../checkbox'
|
|
|
166
169
|
import { UDropdown } from '../dropdown'
|
|
167
170
|
import { UEmpty } from '../empty'
|
|
168
171
|
import { UIcon } from '../icon'
|
|
172
|
+
import { ULoading } from '../loading'
|
|
169
173
|
import { UScroll } from '../scroll'
|
|
170
174
|
import { useOptions } from '../select/use-options'
|
|
171
175
|
import { UTag } from '../tag'
|
|
@@ -204,7 +208,12 @@ const scrollRef = shallowRef<ScrollExposed>()
|
|
|
204
208
|
|
|
205
209
|
const hovered = shallowRef(false)
|
|
206
210
|
|
|
207
|
-
const {
|
|
211
|
+
const {
|
|
212
|
+
options: rawOptions,
|
|
213
|
+
queryString,
|
|
214
|
+
loading,
|
|
215
|
+
allOptions: rawAllOptions
|
|
216
|
+
} = useOptions({ props })
|
|
208
217
|
|
|
209
218
|
const createdOptions = shallowRef<Record<string, any>[]>([])
|
|
210
219
|
|
|
@@ -170,4 +170,10 @@ $root-name: multi-select;
|
|
|
170
170
|
text-align: center;
|
|
171
171
|
padding: fn.use-var(gap, default) 0;
|
|
172
172
|
}
|
|
173
|
+
|
|
174
|
+
@include m.e(loading) {
|
|
175
|
+
// 与选项内容区同高,加载期间保持面板形态,避免塌缩成贴着输入框的小条
|
|
176
|
+
@include m.flex($justify: center, $align: center);
|
|
177
|
+
min-height: 240px;
|
|
178
|
+
}
|
|
173
179
|
}
|
|
@@ -71,15 +71,19 @@
|
|
|
71
71
|
{{ allExpanded ? '收起全部' : '展开全部' }}
|
|
72
72
|
</u-button>
|
|
73
73
|
</div>
|
|
74
|
+
<div v-if="remoteLoading" :class="cls.e('loading')">
|
|
75
|
+
<ULoading type="dual-ring" />
|
|
76
|
+
</div>
|
|
74
77
|
<!-- 菜单列表 -->
|
|
75
78
|
<u-tree
|
|
79
|
+
v-else
|
|
76
80
|
v-bind="treeProps"
|
|
77
81
|
v-model:checked="model"
|
|
78
82
|
@update:checked="handleCheck"
|
|
79
83
|
ref="treeRef"
|
|
80
84
|
:class="cls.e('content-tree')"
|
|
81
85
|
checkable
|
|
82
|
-
:data="
|
|
86
|
+
:data="treeData"
|
|
83
87
|
:slots="slots"
|
|
84
88
|
scroll-to-view
|
|
85
89
|
></u-tree>
|
|
@@ -100,7 +104,7 @@
|
|
|
100
104
|
</template>
|
|
101
105
|
|
|
102
106
|
<script lang="ts" setup>
|
|
103
|
-
import { dfs, o } from '@cat-kit/core'
|
|
107
|
+
import { debounce, dfs, o } from '@cat-kit/core'
|
|
104
108
|
import { useFormFallbackProps } from '@veltra/compositions'
|
|
105
109
|
import { ArrowDown, Close } from '@veltra/icons/normal'
|
|
106
110
|
import { bem, fieldKey, FORM_EMPTY_CONTENT } from '@veltra/utils'
|
|
@@ -113,6 +117,7 @@ import { UButton } from '../button'
|
|
|
113
117
|
import { UCheckbox } from '../checkbox'
|
|
114
118
|
import { UDropdown } from '../dropdown'
|
|
115
119
|
import { UIcon } from '../icon'
|
|
120
|
+
import { ULoading } from '../loading'
|
|
116
121
|
import { UTag } from '../tag'
|
|
117
122
|
import { UTree } from '../tree'
|
|
118
123
|
import type { TreeSlotsScope } from '../tree/di'
|
|
@@ -148,6 +153,7 @@ const treeProps = computed(() => {
|
|
|
148
153
|
'label',
|
|
149
154
|
'readonly',
|
|
150
155
|
'modelValue',
|
|
156
|
+
'data',
|
|
151
157
|
'contentClass',
|
|
152
158
|
'contentStyle',
|
|
153
159
|
'minWidth',
|
|
@@ -159,11 +165,56 @@ const emit = defineEmits<MultiTreeSelectEmits>()
|
|
|
159
165
|
|
|
160
166
|
const slots = defineSlots<{ default?: (props: TreeSlotsScope) => any }>()
|
|
161
167
|
|
|
162
|
-
|
|
168
|
+
/** 是否可搜索:显式声明或数据源为远程函数 */
|
|
169
|
+
const filterable = computed(() => props.filterable || typeof props.data === 'function')
|
|
170
|
+
|
|
171
|
+
/** 过滤 */
|
|
163
172
|
const qs = shallowRef('')
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
173
|
+
|
|
174
|
+
const treeRef = shallowRef<TreeExposed>()
|
|
175
|
+
|
|
176
|
+
/** 远程搜索返回的树数据 */
|
|
177
|
+
const remoteData = shallowRef<Record<string, any>[]>()
|
|
178
|
+
|
|
179
|
+
/** 面板渲染的树数据:data 为函数时来自远程搜索,否则为静态数据 */
|
|
180
|
+
const treeData = computed(() => (typeof props.data === 'function' ? remoteData.value : props.data))
|
|
181
|
+
|
|
182
|
+
const remoteLoading = shallowRef(false)
|
|
183
|
+
/** 远程请求序号;响应返回时序号不一致说明已有更新的请求,结果作废 */
|
|
184
|
+
let remoteSeq = 0
|
|
185
|
+
|
|
186
|
+
const debouncedRemoteSearch = debounce(
|
|
187
|
+
async (
|
|
188
|
+
query: string,
|
|
189
|
+
dataFn: (qs: string) => Promise<Record<string, any>[]> | Record<string, any>[]
|
|
190
|
+
) => {
|
|
191
|
+
const seq = ++remoteSeq
|
|
192
|
+
remoteLoading.value = true
|
|
193
|
+
try {
|
|
194
|
+
const data = await dataFn(query)
|
|
195
|
+
// 响应到达时查询词可能已变化或已有更新的请求,过期结果直接丢弃
|
|
196
|
+
if (seq !== remoteSeq || query !== qs.value) return
|
|
197
|
+
remoteData.value = data ?? []
|
|
198
|
+
// 搜索结果展示完整命中路径,与本地过滤自动展开祖先的行为对齐
|
|
199
|
+
if (query) nextTick(() => treeRef.value?.expandAll())
|
|
200
|
+
} finally {
|
|
201
|
+
if (seq === remoteSeq) remoteLoading.value = false
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
200
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
watch(
|
|
208
|
+
[qs, () => props.data],
|
|
209
|
+
([query, data]) => {
|
|
210
|
+
if (typeof data === 'function') {
|
|
211
|
+
debouncedRemoteSearch(query, data)
|
|
212
|
+
return
|
|
213
|
+
}
|
|
214
|
+
treeRef.value?.filter(query)
|
|
215
|
+
},
|
|
216
|
+
{ immediate: true }
|
|
217
|
+
)
|
|
167
218
|
|
|
168
219
|
const model = defineModel<(string | number)[]>({ default: () => [] })
|
|
169
220
|
|
|
@@ -179,8 +230,6 @@ const showClear = computed(() => {
|
|
|
179
230
|
return props.clearable && model.value?.length && hovered.value && !disabled.value
|
|
180
231
|
})
|
|
181
232
|
|
|
182
|
-
const treeRef = shallowRef<TreeExposed>()
|
|
183
|
-
|
|
184
233
|
const dropdownRef = shallowRef<DropdownExposed>()
|
|
185
234
|
|
|
186
235
|
let changedByEvent = false
|
|
@@ -237,7 +286,7 @@ const handleClear = () => {
|
|
|
237
286
|
const keyDicts = shallowRef(new Map<string | number, Record<string, any>>())
|
|
238
287
|
|
|
239
288
|
watch(
|
|
240
|
-
|
|
289
|
+
treeData,
|
|
241
290
|
(data) => {
|
|
242
291
|
if (!data?.length) {
|
|
243
292
|
keyDicts.value = new Map()
|
|
@@ -126,4 +126,10 @@ $root-name: multi-tree-select;
|
|
|
126
126
|
@include m.e(content-tree) {
|
|
127
127
|
max-height: 240px;
|
|
128
128
|
}
|
|
129
|
+
|
|
130
|
+
@include m.e(loading) {
|
|
131
|
+
// 与树内容区同高,加载期间保持面板形态,避免塌缩成贴着输入框的小条
|
|
132
|
+
@include m.flex($justify: center, $align: center);
|
|
133
|
+
min-height: 240px;
|
|
134
|
+
}
|
|
129
135
|
}
|