@netang/quasar 0.0.24 → 0.0.27

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.
@@ -1,35 +1,65 @@
1
1
  <template>
2
2
  <q-field
3
- :model-value="modelValue"
4
- @clear="onClear"
3
+ class="n-field-tree"
4
+ :model-value="showValue"
5
+ :readonly="readonly"
6
+ :clearable="clearable && (! multiple || collapseTags)"
7
+ @focus="onFieldFocus"
8
+ @blur="onFieldBlur"
9
+ @clear="onFieldClear"
5
10
  v-bind="$attrs"
6
11
  >
7
12
  <template v-slot:control>
8
13
 
9
- <!-- 如果有值 -->
10
- <template v-if="treeTickedNodes.length">
11
-
12
- <!-- 如果显示单个值 -->
13
- <div v-if="! multiple">{{treeTickedNodes[0].label}}</div>
14
-
15
- <!-- 显示折叠的值数量 -->
16
- <div v-else-if="collapseTags">
17
- <q-chip dense>+{{treeTickedNodes.length}}</q-chip>
18
- </div>
19
-
20
- <!-- 否则显示多个值 -->
21
- <q-chip
22
- v-for="(item, itemIndex) in treeTickedNodes"
23
- :key="`item-${item.id}`"
24
- @remove="onRemoveItem(itemIndex)"
25
- removable
26
- dense
27
- v-else
28
- >{{item.label}}</q-chip>
14
+ <template v-if="multiple">
15
+
16
+ <template v-if="treeTickedNodes.length">
17
+
18
+ <!-- 多选插槽 -->
19
+ <slot
20
+ name="selected"
21
+ :ticked="treeTickedNodes"
22
+ :remove="onRemoveItem"
23
+ v-if="$slots.ticked"
24
+ />
25
+
26
+ <!-- 显示折叠的值数量 -->
27
+ <q-chip
28
+ dense
29
+ :label="`+${treeTickedNodes.length}`"
30
+ v-else-if="collapseTags"
31
+ />
32
+
33
+ <!-- 多选标签 -->
34
+ <template v-else>
35
+ <q-chip
36
+ v-for="(item, index) in treeTickedNodes"
37
+ :key="`item-${index}`"
38
+ :label="item.label"
39
+ dense
40
+ removable
41
+ @remove="onRemoveItem(index)"
42
+ />
43
+ </template>
44
+ </template>
45
+
46
+ <!-- 占位符-->
47
+ <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
29
48
  </template>
30
49
 
31
- <!-- 否则显示占位符 -->
32
- <div class="n-placeholder" v-else-if="placeholder">{{placeholder}}</div>
50
+ <!-- 显示文字 -->
51
+ <span v-else-if="showValue">{{showValue}}</span>
52
+
53
+ <!-- 占位符-->
54
+ <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
55
+
56
+ <!-- 筛选输入框 -->
57
+ <input
58
+ ref="inputRef"
59
+ class="q-field__input q-placeholder col q-field__input--padding"
60
+ v-model="inputValue"
61
+ v-if="filter"
62
+ />
33
63
  </template>
34
64
 
35
65
  <template v-slot:before v-if="$slots.before">
@@ -50,6 +80,10 @@
50
80
  ref="popupRef"
51
81
  no-refocus
52
82
  no-focus
83
+ fit
84
+ @focus="onPopupFocus"
85
+ @show="onPopupShow"
86
+ @before-hide="onPopupBeforeHide"
53
87
  v-if="! readonly"
54
88
  >
55
89
  <q-card>
@@ -58,6 +92,7 @@
58
92
  class="q-pa-sm q-pr-md"
59
93
  style="min-width:260px;"
60
94
  ref="treeRef"
95
+ :filter="inputValue"
61
96
  :nodes="nodes"
62
97
  :node-key="nodeKey"
63
98
  :label-key="labelKey"
@@ -83,7 +118,7 @@
83
118
  </template>
84
119
 
85
120
  <script>
86
- import { ref, computed, watch } from 'vue'
121
+ import { ref, computed, watch, onMounted, onUpdated } from 'vue'
87
122
 
88
123
  export default {
89
124
 
@@ -96,14 +131,10 @@ export default {
96
131
  * 声明属性
97
132
  */
98
133
  props: {
99
- // 是否只读
100
- readonly: Boolean,
101
134
  // 值
102
135
  modelValue: [Array, String, Number],
103
136
  // 树展开节点
104
137
  expanded: Array, // v-model:expanded
105
- // 占位符
106
- placeholder: String,
107
138
  // 节点数组
108
139
  nodes: Array,
109
140
  // 唯一的节点 id
@@ -127,8 +158,17 @@ export default {
127
158
  type: Boolean,
128
159
  default: true,
129
160
  },
161
+
162
+ // 是否开启筛选
163
+ filter: Boolean,
130
164
  // 多选模式下是否折叠 Tag
131
165
  collapseTags: Boolean,
166
+ // 占位符
167
+ placeholder: String,
168
+ // 是否可清除
169
+ clearable: Boolean,
170
+ // 是否只读
171
+ readonly: Boolean,
132
172
  },
133
173
 
134
174
  /**
@@ -143,16 +183,44 @@ export default {
143
183
  */
144
184
  setup(props, { emit }) {
145
185
 
186
+ // ==========【计算属性】=========================================================================================
187
+
188
+ /**
189
+ * 显示值
190
+ */
191
+ const showValue = computed(function () {
192
+
193
+ // 如果有已选数据
194
+ return utils.isValidArray(treeTickedNodes.value)
195
+ // 取已选数据第一条的标签
196
+ ? treeTickedNodes.value[0].label
197
+ : ''
198
+ })
199
+
146
200
  // ==========【数据】============================================================================================
147
201
 
202
+
203
+ // 输入框节点
204
+ const inputRef = ref(null)
205
+
206
+ // 输入框值
207
+ const inputValue = ref('')
208
+
148
209
  // 弹出层节点
149
210
  const popupRef = ref(null)
211
+
212
+ // 是否显示弹出层
213
+ const showPopup = ref(false)
214
+
150
215
  // 树节点
151
216
  const treeRef = ref(null)
217
+
152
218
  // 树选择数据
153
219
  const treeTicked = ref(formatModelValue())
220
+
154
221
  // tree all
155
222
  let treeAll = getTreeAll()
223
+
156
224
  // 树展开数据
157
225
  const treeExpanded = ref(getExpanded())
158
226
 
@@ -202,7 +270,30 @@ export default {
202
270
  * 监听声明值
203
271
  */
204
272
  watch(()=>props.modelValue, function() {
273
+
274
+ // 设置选中数据
205
275
  treeTicked.value = formatModelValue()
276
+
277
+ // 设置输入框焦点
278
+ setInputFocus()
279
+
280
+ // 设置输入框文字选中
281
+ setInputSelection()
282
+ })
283
+
284
+ /**
285
+ * 监听输入框值
286
+ */
287
+ watch(inputValue, function (val) {
288
+ if (
289
+ // 如果弹出层是隐藏的
290
+ ! showPopup.value
291
+ // 如果输入框有值
292
+ && utils.isValidValue(val)
293
+ ) {
294
+ // 显示弹出层
295
+ popupRef.value.show()
296
+ }
206
297
  })
207
298
 
208
299
  // ==========【方法】=============================================================================================
@@ -370,16 +461,137 @@ export default {
370
461
  }
371
462
 
372
463
  /**
373
- * 清空
464
+ * 字段获取焦点触发
465
+ */
466
+ function onFieldFocus(e) {
467
+
468
+ // 停止冒泡
469
+ e.stopPropagation()
470
+
471
+ // 设置输入框焦点
472
+ setInputFocus()
473
+
474
+ window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0)
475
+ }
476
+
477
+ /**
478
+ * 字段失去焦点触发
479
+ */
480
+ function onFieldBlur(e) {
481
+
482
+ // 停止冒泡
483
+ e.stopPropagation()
484
+
485
+ if (
486
+ // 如果开启筛选
487
+ props.filter
488
+ // 如果没有显示弹出层
489
+ && ! showPopup.value
490
+ ) {
491
+ // 清空输入框值
492
+ inputValue.value = ''
493
+ }
494
+ }
495
+
496
+ /**
497
+ * 字段清空触发
374
498
  */
375
- function onClear() {
499
+ function onFieldClear() {
500
+
501
+ // 更新值
376
502
  emit('update:modelValue', props.multiple ? [] : null)
503
+
504
+ // 隐藏弹出层
377
505
  popupRef.value.hide()
378
506
  }
379
507
 
508
+ /**
509
+ * 弹出层获取焦点触发
510
+ */
511
+ function onPopupFocus(e) {
512
+
513
+ // 停止冒泡
514
+ e.stopPropagation()
515
+
516
+ // 设置输入框焦点
517
+ setInputFocus()
518
+
519
+ window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0)
520
+ }
521
+
522
+ /**
523
+ * 弹出层显示回调
524
+ */
525
+ function onPopupShow() {
526
+
527
+ // 显示弹出层
528
+ showPopup.value = true
529
+
530
+ // 设置输入框焦点
531
+ setInputFocus()
532
+ }
533
+
534
+ /**
535
+ * 弹出层隐藏前显示回调
536
+ */
537
+ function onPopupBeforeHide() {
538
+
539
+ // 隐藏弹出层
540
+ showPopup.value = false
541
+ }
542
+
543
+ /**
544
+ * 设置输入框文字选中
545
+ */
546
+ function setInputSelection() {
547
+ if (
548
+ // 如果开启筛选
549
+ props.filter
550
+ // 如果有输入框节点
551
+ && inputRef.value
552
+ // 如果输入框有值
553
+ && inputValue.value.length
554
+ ) {
555
+ // 全选文字
556
+ inputRef.value.select()
557
+ // inputRef.value.setSelectionRange(0, inputValue.value.length)
558
+ }
559
+ }
560
+
561
+ /**
562
+ * 设置输入框焦点
563
+ */
564
+ function setInputFocus() {
565
+ if (
566
+ // 如果开启筛选
567
+ props.filter
568
+ // 如果有输入框节点
569
+ && inputRef.value
570
+ ) {
571
+ inputRef.value.focus()
572
+ }
573
+ }
574
+
575
+ // ==========【生命周期】=========================================================================================
576
+
577
+ /**
578
+ * 在组件因为响应式状态变更而更新其 DOM 树之后调用
579
+ */
580
+ onUpdated(function () {
581
+ if (_.has(popupRef.value, 'currentComponent.ref.updatePosition')) {
582
+ popupRef.value.currentComponent.ref.updatePosition()
583
+ }
584
+ })
585
+
380
586
  // ==========【返回】=============================================================================================
381
587
 
382
588
  return {
589
+ // 显示值
590
+ showValue,
591
+ // 输入框节点
592
+ inputRef,
593
+ // 输入框值
594
+ inputValue,
383
595
  // 弹出层节点
384
596
  popupRef,
385
597
  // 树节点
@@ -397,11 +609,35 @@ export default {
397
609
  onUpdateTicked,
398
610
  // 点击节点
399
611
  onNode,
400
- // 清空
401
- onClear,
402
612
  // 移除单个
403
613
  onRemoveItem,
614
+
615
+ // 字段获取焦点触发
616
+ onFieldFocus,
617
+ // 字段失去焦点触发
618
+ onFieldBlur,
619
+ // 字段清空触发
620
+ onFieldClear,
621
+
622
+ // 弹出层获取焦点触发
623
+ onPopupFocus,
624
+ // 弹出层显示回调
625
+ onPopupShow,
626
+ // 弹出层隐藏前显示回调
627
+ onPopupBeforeHide,
404
628
  }
405
629
  },
406
630
  }
407
631
  </script>
632
+
633
+ <style lang="scss">
634
+ @import "@/assets/sass/var.scss";
635
+
636
+ .n-field-tree {
637
+ .q-field__input--padding {
638
+ padding-left: 4px;
639
+ min-width: 50px !important;
640
+ cursor: text;
641
+ }
642
+ }
643
+ </style>