@netang/quasar 0.0.24 → 0.0.25

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.
@@ -2,41 +2,56 @@
2
2
  <!--:class="fieldFocused ? 'q-field&#45;&#45;float q-field&#45;&#45;focused q-field&#45;&#45;highlighted' : ''"-->
3
3
  <q-field
4
4
  class="n-field-table"
5
- :model-value="modelValue"
5
+ :model-value="showValue"
6
6
  :readonly="readonly"
7
- :clearable="clearable && ! multiple"
7
+ :clearable="clearable && (! multiple || collapseTags)"
8
+ @focus="onFieldFocus"
9
+ @blur="onFieldBlur"
8
10
  @clear="onFieldClear"
9
11
  v-bind="$attrs"
10
12
  >
11
13
  <template v-slot:control>
12
14
 
13
15
  <template v-if="multiple">
16
+ <template v-if="selected.length">
17
+
18
+ <!-- 多选插槽 -->
19
+ <slot
20
+ name="selected"
21
+ :selected="selected"
22
+ :remove="onRemoveSelected"
23
+ v-if="$slots.selected"
24
+ />
14
25
 
15
- <!-- 多选插槽 -->
16
- <slot
17
- name="selected"
18
- :selected="selected"
19
- :remove="onRemoveSelected"
20
- v-if="$slots.selected"
21
- />
22
-
23
- <!-- 多选标签 -->
24
- <template v-else>
26
+ <!-- 显示折叠的值数量 -->
25
27
  <q-chip
26
- v-for="(item, index) in selected"
27
- :key="`options-${index}`"
28
- :label="item[labelKey || rowKey]"
29
- removable
30
- @remove="onRemoveSelected(item, index)"
31
28
  dense
29
+ :label="`+${selected.length}`"
30
+ v-else-if="collapseTags"
32
31
  />
32
+
33
+ <!-- 多选标签 -->
34
+ <template v-else>
35
+ <q-chip
36
+ v-for="(item, index) in selected"
37
+ :key="`options-${index}`"
38
+ :label="currentFormatLabel(item)"
39
+ dense
40
+ removable
41
+ @remove="onRemoveSelected(index)"
42
+ />
43
+ </template>
33
44
  </template>
45
+
46
+ <!-- 占位符-->
47
+ <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
34
48
  </template>
35
49
 
36
50
  <!-- 显示文字 -->
37
- <span v-else>
38
- {{showValue}}
39
- </span>
51
+ <span v-else-if="showValue">{{showValue}}</span>
52
+
53
+ <!-- 占位符-->
54
+ <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
40
55
 
41
56
  <!-- 筛选输入框 -->
42
57
  <input
@@ -62,8 +77,10 @@
62
77
  ref="popupRef"
63
78
  no-refocus
64
79
  no-focus
65
- @show="onPopupShow"
80
+ fit
66
81
  @focus="onPopupFocus"
82
+ @show="onPopupShow"
83
+ @before-hide="onPopupBeforeHide"
67
84
  v-if="! readonly"
68
85
  >
69
86
  <!-- 快捷表格 -->
@@ -142,7 +159,7 @@
142
159
  </template>
143
160
 
144
161
  <script>
145
- import { ref, computed, watch, onMounted } from 'vue'
162
+ import { ref, computed, watch, onMounted, onUpdated } from 'vue'
146
163
 
147
164
  export default {
148
165
 
@@ -157,8 +174,6 @@ export default {
157
174
  props: {
158
175
  // 值
159
176
  modelValue: [ String, Number, Array ],
160
- // 是否可清除
161
- clearable: Boolean,
162
177
  // 表格请求路径
163
178
  path: String,
164
179
  // 表格请求参数
@@ -177,16 +192,10 @@ export default {
177
192
  type: String,
178
193
  required: true,
179
194
  },
180
- // 标签字段(必填)
181
- labelKey: {
182
- type: String,
183
- required: true,
184
- },
185
- // 表格行唯一键值
186
- rowKey: {
187
- type: String,
188
- default: 'id',
189
- },
195
+ // 标签字段
196
+ labelKey: String,
197
+ // 格式化显示标签
198
+ formatLabel: Function,
190
199
  // 快捷表格显示的字段数组(空为:[值字段, 标签字段])
191
200
  showKeys: Array,
192
201
  // 隐藏搜索字段数组
@@ -200,10 +209,6 @@ export default {
200
209
  // 对话框声明属性
201
210
  dialogProps: Object,
202
211
 
203
- // 占位符
204
- placeholder: String,
205
- // 是否只读
206
- readonly: Boolean,
207
212
  // 值是否为数组
208
213
  valueArray: Boolean,
209
214
  // 关闭对话框
@@ -215,6 +220,14 @@ export default {
215
220
  rows: Array,
216
221
  // 是否多选
217
222
  multiple: Boolean,
223
+ // 多选模式下是否折叠 Tag
224
+ collapseTags: Boolean,
225
+ // 占位符
226
+ placeholder: String,
227
+ // 是否可清除
228
+ clearable: Boolean,
229
+ // 是否只读
230
+ readonly: Boolean,
218
231
  // 输入防抖(毫秒)
219
232
  inputDebounce: {
220
233
  type: [ Number, String ],
@@ -244,20 +257,27 @@ export default {
244
257
  return utils.isValidObject(slots) ? Object.keys(slots) : []
245
258
  })
246
259
 
260
+ /**
261
+ * 当前标签字段
262
+ */
263
+ const currentlabelKey = computed(function() {
264
+ return props.labelKey || props.valueKey
265
+ })
266
+
247
267
  /**
248
268
  * 当前显示字段
249
269
  */
250
270
  const currentShowKeys = computed(function() {
251
- return utils.isValidArray(props.showKeys)
271
+ return _.uniq(utils.isValidArray(props.showKeys)
252
272
  ? props.showKeys
253
- : [ props.rowKey, props.labelKey ]
273
+ : [ props.valueKey, currentlabelKey.value ])
254
274
  })
255
275
 
256
276
  /**
257
277
  * 当前搜索字段
258
278
  */
259
- const currentFilterKeys = computed(function() {
260
- return props.filterKey || props.labelKey
279
+ const currentFilterKey = computed(function() {
280
+ return props.filterKey || currentlabelKey.value
261
281
  })
262
282
 
263
283
  /**
@@ -265,12 +285,11 @@ export default {
265
285
  */
266
286
  const showValue = computed(function () {
267
287
 
268
- // 如果是单选
269
- if (! props.multiple && utils.isValidArray(selected.value)) {
270
- return selected.value[0][props.labelKey || props.rowKey]
271
- }
272
-
273
- return ''
288
+ // 如果有已选数据
289
+ return utils.isValidArray(selected.value)
290
+ // 取已选数据第一条
291
+ ? currentFormatLabel(selected.value[0])
292
+ : ''
274
293
  })
275
294
 
276
295
  // ==========【数据】============================================================================================
@@ -299,7 +318,7 @@ export default {
299
318
  // 获取表格列数据
300
319
  columns: getTableColumns(),
301
320
  // 表格行唯一键值
302
- rowKey: props.rowKey,
321
+ rowKey: props.valueKey,
303
322
  // 行数据
304
323
  rows: props.rows,
305
324
  // 选择类型, 可选值 single multiple none
@@ -333,6 +352,9 @@ export default {
333
352
  // 是否显示对话框
334
353
  const showDialog = ref(false)
335
354
 
355
+ // 是否显示弹出层
356
+ const showPopup = ref(false)
357
+
336
358
  // 当前已选数据
337
359
  const selected = ref([...$table.tableSelected.value])
338
360
 
@@ -366,7 +388,7 @@ export default {
366
388
  // 已选数据值数组
367
389
  const selectedValues = utils.isValidArray(selected.value)
368
390
  // 如果有已选数据
369
- ? _.uniq(selected.value.map(e => e[props.rowKey]))
391
+ ? _.uniq(selected.value.map(e => e[props.valueKey]))
370
392
  // 否则为空
371
393
  : []
372
394
 
@@ -374,7 +396,7 @@ export default {
374
396
  const removeValues = selectedValues.filter(e => values.indexOf(e) === -1)
375
397
  if (removeValues.length) {
376
398
  utils.forEachRight(selected.value, function (item, index) {
377
- if (removeValues.indexOf(item[props.rowKey]) > -1) {
399
+ if (removeValues.indexOf(item[props.valueKey]) > -1) {
378
400
  selected.value.splice(index, 1)
379
401
  }
380
402
  })
@@ -395,13 +417,17 @@ export default {
395
417
  if (val !== selected.value) {
396
418
  // 设置选择数据
397
419
  selected.value = val
420
+
421
+ // 检查值更新
422
+ checkModelValueChange()
398
423
  }
399
- // 检查值更新
400
- checkModelValueChange()
401
424
 
402
425
  // 设置输入框焦点
403
426
  setInputFocus()
404
427
 
428
+ // 设置输入框文字选中
429
+ setInputSelection()
430
+
405
431
  }, {
406
432
  // 深度监听
407
433
  deep: true
@@ -412,8 +438,19 @@ export default {
412
438
  */
413
439
  watch(selected, function(val) {
414
440
  if (val !== props.selected) {
441
+
442
+ // 更新选择数据
415
443
  emit('update:selected', val)
444
+
445
+ // 检查值更新
446
+ checkModelValueChange()
416
447
  }
448
+
449
+ // 设置输入框焦点
450
+ setInputFocus()
451
+
452
+ // 设置输入框文字选中
453
+ setInputSelection()
417
454
  }, {
418
455
  // 深度监听
419
456
  deep: true,
@@ -427,10 +464,11 @@ export default {
427
464
  // 取消延迟执行
428
465
  sleep.cancel()
429
466
 
430
- if (utils.isValidValue(val)) {
467
+ const hasValue = utils.isValidValue(val)
468
+ if (hasValue) {
431
469
 
432
470
  const n_search = {}
433
- n_search[currentFilterKeys.value] = [
471
+ n_search[currentFilterKey.value] = [
434
472
  {
435
473
  // 比较类型
436
474
  type: dicts.SEARCH_TYPE__LIKE,
@@ -450,12 +488,34 @@ export default {
450
488
  // 延迟执行
451
489
  await sleep(props.inputDebounce)
452
490
 
491
+ if (
492
+ // 如果弹出层是隐藏的
493
+ ! showPopup.value
494
+ // 如果输入框有值
495
+ && hasValue
496
+ ) {
497
+ // 显示弹出层
498
+ popupRef.value.show()
499
+ }
500
+
453
501
  // 表格重新加载
454
502
  await $table.tableReload()
455
503
  })
456
504
 
457
505
  // ==========【方法】=============================================================================================
458
506
 
507
+ /**
508
+ * 当前格式化显示标签
509
+ */
510
+ function currentFormatLabel(item) {
511
+ // 如果有格式化显示标签方法
512
+ return _.isFunction(props.formatLabel)
513
+ // 执行格式化显示标签方法
514
+ ? props.formatLabel(item)
515
+ // 否则显示该值的标签字段
516
+ : item[currentlabelKey.value]
517
+ }
518
+
459
519
  /**
460
520
  * 格式化值
461
521
  */
@@ -497,7 +557,7 @@ export default {
497
557
  // 查看字段
498
558
  n_view: {
499
559
  // 查看字段
500
- field: props.rowKey,
560
+ field: props.valueKey,
501
561
  // 查看值
502
562
  value,
503
563
  },
@@ -548,9 +608,9 @@ export default {
548
608
  ? (
549
609
  props.multiple
550
610
  // 如果是多选
551
- ? selected.value.map(e => e[props.rowKey])
611
+ ? selected.value.map(e => e[props.valueKey])
552
612
  // 否则是单选
553
- : [ selected.value[0][props.rowKey] ]
613
+ : [ selected.value[0][props.valueKey] ]
554
614
  )
555
615
  // 否则为空
556
616
  : []
@@ -642,10 +702,43 @@ export default {
642
702
  /**
643
703
  * 移除已选数据
644
704
  */
645
- function onRemoveSelected(item, index) {
705
+ function onRemoveSelected(index) {
646
706
  selected.value.splice(index, 1)
647
707
  }
648
708
 
709
+ /**
710
+ * 字段获取焦点触发
711
+ */
712
+ function onFieldFocus(e) {
713
+
714
+ // 停止冒泡
715
+ e.stopPropagation()
716
+
717
+ // 设置输入框焦点
718
+ setInputFocus()
719
+
720
+ window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0)
721
+ }
722
+
723
+ /**
724
+ * 字段失去焦点触发
725
+ */
726
+ function onFieldBlur(e) {
727
+
728
+ // 停止冒泡
729
+ e.stopPropagation()
730
+
731
+ if (
732
+ // 如果开启筛选
733
+ props.filter
734
+ // 如果没有显示弹出层
735
+ && ! showPopup.value
736
+ ) {
737
+ // 清空输入框值
738
+ inputValue.value = ''
739
+ }
740
+ }
741
+
649
742
  /**
650
743
  * 字段清空触发
651
744
  */
@@ -654,34 +747,47 @@ export default {
654
747
  // 清空快捷表格已选数据
655
748
  selected.value = []
656
749
 
657
- // 关闭弹出层
750
+ // 隐藏弹出层
658
751
  popupRef.value.hide()
659
752
  }
660
753
 
661
754
  /**
662
- * 弹出层显示回调
755
+ * 弹出层获取焦点触发
663
756
  */
664
- function onPopupShow() {
757
+ function onPopupFocus(e) {
665
758
 
666
- // 表格加载(只加载一次)
667
- $table.tableLoad()
759
+ // 停止冒泡
760
+ e.stopPropagation()
668
761
 
669
762
  // 设置输入框焦点
670
763
  setInputFocus()
764
+
765
+ window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0)
671
766
  }
672
767
 
673
768
  /**
674
- * 弹出层获取焦点触发
769
+ * 弹出层显示回调
675
770
  */
676
- function onPopupFocus(e) {
771
+ function onPopupShow() {
677
772
 
678
- // 停止冒泡
679
- e.stopPropagation()
773
+ // 显示弹出层
774
+ showPopup.value = true
680
775
 
681
776
  // 设置输入框焦点
682
777
  setInputFocus()
683
778
 
684
- window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0)
779
+ // 表格加载(只加载一次)
780
+ $table.tableLoad()
781
+ .finally()
782
+ }
783
+
784
+ /**
785
+ * 弹出层隐藏前显示回调
786
+ */
787
+ function onPopupBeforeHide() {
788
+
789
+ // 隐藏弹出层
790
+ showPopup.value = false
685
791
  }
686
792
 
687
793
  /**
@@ -692,6 +798,7 @@ export default {
692
798
  // 设置当前已选数据
693
799
  $table.tableSelected.value = [...selected.value]
694
800
 
801
+ // 隐藏弹出层
695
802
  popupRef.value.hide()
696
803
  }
697
804
 
@@ -699,7 +806,10 @@ export default {
699
806
  * 对话框显示回调
700
807
  */
701
808
  function onDialogShow() {
809
+
810
+ // 表格加载(只加载一次)
702
811
  $table.tableLoad()
812
+ .finally()
703
813
  }
704
814
 
705
815
  /**
@@ -710,7 +820,12 @@ export default {
710
820
  let isReload = true
711
821
 
712
822
  // 清空输入框值
713
- if (inputValue.value) {
823
+ if (
824
+ // 如果开启筛选
825
+ props.filter
826
+ // 如果有输入框值
827
+ && inputValue.value
828
+ ) {
714
829
  // 此时清空输入框后, 会自动刷新表格
715
830
  inputValue.value = ''
716
831
 
@@ -741,7 +856,7 @@ export default {
741
856
  if (props.multiple) {
742
857
 
743
858
  const opt = {}
744
- opt[props.rowKey] = row[props.rowKey]
859
+ opt[props.valueKey] = row[props.valueKey]
745
860
 
746
861
  // 获取当前数据索引
747
862
  const itemIndex = _.findIndex(selected.value, opt)
@@ -760,15 +875,40 @@ export default {
760
875
  // 否则为单选
761
876
  } else {
762
877
  selected.value = [ row ]
878
+
879
+ // 隐藏弹出层
763
880
  popupRef.value.hide()
764
881
  }
765
882
  }
766
883
 
884
+ /**
885
+ * 设置输入框文字选中
886
+ */
887
+ function setInputSelection() {
888
+ if (
889
+ // 如果开启筛选
890
+ props.filter
891
+ // 如果有输入框节点
892
+ && inputRef.value
893
+ // 如果输入框有值
894
+ && inputValue.value.length
895
+ ) {
896
+ // 全选文字
897
+ inputRef.value.select()
898
+ // inputRef.value.setSelectionRange(0, inputValue.value.length)
899
+ }
900
+ }
901
+
767
902
  /**
768
903
  * 设置输入框焦点
769
904
  */
770
905
  function setInputFocus() {
771
- if (inputRef.value) {
906
+ if (
907
+ // 如果开启筛选
908
+ props.filter
909
+ // 如果有输入框节点
910
+ && inputRef.value
911
+ ) {
772
912
  inputRef.value.focus()
773
913
  }
774
914
  }
@@ -784,6 +924,15 @@ export default {
784
924
  await onLoadSelected()
785
925
  })
786
926
 
927
+ /**
928
+ * 在组件因为响应式状态变更而更新其 DOM 树之后调用
929
+ */
930
+ onUpdated(function () {
931
+ if (_.has(popupRef.value, 'currentComponent.ref.updatePosition')) {
932
+ popupRef.value.currentComponent.ref.updatePosition()
933
+ }
934
+ })
935
+
787
936
  // ==========【返回】=============================================================================================
788
937
 
789
938
  return {
@@ -792,6 +941,8 @@ export default {
792
941
 
793
942
  // 插槽标识
794
943
  slotNames,
944
+ // 当前标签字段
945
+ currentlabelKey,
795
946
  // 显示值
796
947
  showValue,
797
948
 
@@ -808,15 +959,24 @@ export default {
808
959
  // 当前表格列数据
809
960
  columns,
810
961
 
962
+ // 当前格式化显示标签
963
+ currentFormatLabel,
811
964
  // 移除已选数据
812
965
  onRemoveSelected,
966
+
967
+ // 字段获取焦点触发
968
+ onFieldFocus,
969
+ // 字段失去焦点触发
970
+ onFieldBlur,
813
971
  // 字段清空触发
814
972
  onFieldClear,
815
973
 
816
- // 弹出层显示回调
817
- onPopupShow,
818
974
  // 弹出层获取焦点触发
819
975
  onPopupFocus,
976
+ // 弹出层显示回调
977
+ onPopupShow,
978
+ // 弹出层隐藏前显示回调
979
+ onPopupBeforeHide,
820
980
 
821
981
  // 对话框显示前回调
822
982
  onDialogBeforeShow,
@@ -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
  /**
@@ -141,18 +181,53 @@ export default {
141
181
  /**
142
182
  * 组合式
143
183
  */
144
- setup(props, { emit }) {
184
+ setup(props, { emit, slots }) {
185
+
186
+ // ==========【计算属性】=========================================================================================
187
+
188
+ /**
189
+ * 插槽标识
190
+ */
191
+ const slotNames = computed(function() {
192
+ return utils.isValidObject(slots) ? Object.keys(slots) : []
193
+ })
194
+
195
+ /**
196
+ * 显示值
197
+ */
198
+ const showValue = computed(function () {
199
+
200
+ // 如果有已选数据
201
+ return utils.isValidArray(treeTickedNodes.value)
202
+ // 取已选数据第一条的标签
203
+ ? treeTickedNodes.value[0].label
204
+ : ''
205
+ })
145
206
 
146
207
  // ==========【数据】============================================================================================
147
208
 
209
+
210
+ // 输入框节点
211
+ const inputRef = ref(null)
212
+
213
+ // 输入框值
214
+ const inputValue = ref('')
215
+
148
216
  // 弹出层节点
149
217
  const popupRef = ref(null)
218
+
219
+ // 是否显示弹出层
220
+ const showPopup = ref(false)
221
+
150
222
  // 树节点
151
223
  const treeRef = ref(null)
224
+
152
225
  // 树选择数据
153
226
  const treeTicked = ref(formatModelValue())
227
+
154
228
  // tree all
155
229
  let treeAll = getTreeAll()
230
+
156
231
  // 树展开数据
157
232
  const treeExpanded = ref(getExpanded())
158
233
 
@@ -202,7 +277,31 @@ export default {
202
277
  * 监听声明值
203
278
  */
204
279
  watch(()=>props.modelValue, function() {
280
+
281
+ // 设置选中数据
205
282
  treeTicked.value = formatModelValue()
283
+
284
+ // 设置输入框焦点
285
+ setInputFocus()
286
+
287
+ // 设置输入框文字选中
288
+ setInputSelection()
289
+ })
290
+
291
+ /**
292
+ * 监听输入框值
293
+ */
294
+ watch(inputValue, function (val) {
295
+
296
+ if (
297
+ // 如果弹出层是隐藏的
298
+ ! showPopup.value
299
+ // 如果输入框有值
300
+ && utils.isValidValue(val)
301
+ ) {
302
+ // 显示弹出层
303
+ popupRef.value.show()
304
+ }
206
305
  })
207
306
 
208
307
  // ==========【方法】=============================================================================================
@@ -370,16 +469,137 @@ export default {
370
469
  }
371
470
 
372
471
  /**
373
- * 清空
472
+ * 字段获取焦点触发
374
473
  */
375
- function onClear() {
474
+ function onFieldFocus(e) {
475
+
476
+ // 停止冒泡
477
+ e.stopPropagation()
478
+
479
+ // 设置输入框焦点
480
+ setInputFocus()
481
+
482
+ window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0)
483
+ }
484
+
485
+ /**
486
+ * 字段失去焦点触发
487
+ */
488
+ function onFieldBlur(e) {
489
+
490
+ // 停止冒泡
491
+ e.stopPropagation()
492
+
493
+ if (
494
+ // 如果开启筛选
495
+ props.filter
496
+ // 如果没有显示弹出层
497
+ && ! showPopup.value
498
+ ) {
499
+ // 清空输入框值
500
+ inputValue.value = ''
501
+ }
502
+ }
503
+
504
+ /**
505
+ * 字段清空触发
506
+ */
507
+ function onFieldClear() {
508
+
509
+ // 更新值
376
510
  emit('update:modelValue', props.multiple ? [] : null)
511
+
512
+ // 隐藏弹出层
377
513
  popupRef.value.hide()
378
514
  }
379
515
 
516
+ /**
517
+ * 弹出层获取焦点触发
518
+ */
519
+ function onPopupFocus(e) {
520
+
521
+ // 停止冒泡
522
+ e.stopPropagation()
523
+
524
+ // 设置输入框焦点
525
+ setInputFocus()
526
+
527
+ window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0)
528
+ }
529
+
530
+ /**
531
+ * 弹出层显示回调
532
+ */
533
+ function onPopupShow() {
534
+
535
+ // 显示弹出层
536
+ showPopup.value = true
537
+
538
+ // 设置输入框焦点
539
+ setInputFocus()
540
+ }
541
+
542
+ /**
543
+ * 弹出层隐藏前显示回调
544
+ */
545
+ function onPopupBeforeHide() {
546
+
547
+ // 隐藏弹出层
548
+ showPopup.value = false
549
+ }
550
+
551
+ /**
552
+ * 设置输入框文字选中
553
+ */
554
+ function setInputSelection() {
555
+ if (
556
+ // 如果开启筛选
557
+ props.filter
558
+ // 如果有输入框节点
559
+ && inputRef.value
560
+ // 如果输入框有值
561
+ && inputValue.value.length
562
+ ) {
563
+ // 全选文字
564
+ inputRef.value.select()
565
+ // inputRef.value.setSelectionRange(0, inputValue.value.length)
566
+ }
567
+ }
568
+
569
+ /**
570
+ * 设置输入框焦点
571
+ */
572
+ function setInputFocus() {
573
+ if (
574
+ // 如果开启筛选
575
+ props.filter
576
+ // 如果有输入框节点
577
+ && inputRef.value
578
+ ) {
579
+ inputRef.value.focus()
580
+ }
581
+ }
582
+
583
+ // ==========【生命周期】=========================================================================================
584
+
585
+ /**
586
+ * 在组件因为响应式状态变更而更新其 DOM 树之后调用
587
+ */
588
+ onUpdated(function () {
589
+ if (_.has(popupRef.value, 'currentComponent.ref.updatePosition')) {
590
+ popupRef.value.currentComponent.ref.updatePosition()
591
+ }
592
+ })
593
+
380
594
  // ==========【返回】=============================================================================================
381
595
 
382
596
  return {
597
+ // 显示值
598
+ showValue,
599
+ // 输入框节点
600
+ inputRef,
601
+ // 输入框值
602
+ inputValue,
383
603
  // 弹出层节点
384
604
  popupRef,
385
605
  // 树节点
@@ -397,11 +617,35 @@ export default {
397
617
  onUpdateTicked,
398
618
  // 点击节点
399
619
  onNode,
400
- // 清空
401
- onClear,
402
620
  // 移除单个
403
621
  onRemoveItem,
622
+
623
+ // 字段获取焦点触发
624
+ onFieldFocus,
625
+ // 字段失去焦点触发
626
+ onFieldBlur,
627
+ // 字段清空触发
628
+ onFieldClear,
629
+
630
+ // 弹出层获取焦点触发
631
+ onPopupFocus,
632
+ // 弹出层显示回调
633
+ onPopupShow,
634
+ // 弹出层隐藏前显示回调
635
+ onPopupBeforeHide,
404
636
  }
405
637
  },
406
638
  }
407
639
  </script>
640
+
641
+ <style lang="scss">
642
+ @import "@/assets/sass/var.scss";
643
+
644
+ .n-field-tree {
645
+ .q-field__input--padding {
646
+ padding-left: 4px;
647
+ min-width: 50px !important;
648
+ cursor: text;
649
+ }
650
+ }
651
+ </style>
@@ -2,7 +2,7 @@
2
2
  <div class="flex column absolute-full" v-if="utils.isValidArray(options)">
3
3
  <q-scroll-area class="n-flex-1">
4
4
 
5
- <div class="n-search q-pa-sm q-pt-md q-gutter-md">
5
+ <div class="n-search q-pa-sm q-pt-sm q-gutter-sm">
6
6
 
7
7
  <template
8
8
  v-for="(item, itemIndex) in options"
@@ -110,13 +110,23 @@
110
110
  v-model="modelValue[itemIndex][index].value"
111
111
  dense
112
112
  outlined
113
- clearable
114
- accordion
115
113
  :multiple="multiple"
116
114
  v-bind="item.tree"
117
115
  v-else-if="item.searchType === 'tree'"
118
116
  />
119
117
 
118
+ <!-- 下拉表格 -->
119
+ <n-field-table
120
+ class="n-field-fieldset"
121
+ :label="label"
122
+ v-model="modelValue[itemIndex][index].value"
123
+ dense
124
+ outlined
125
+ :multiple="multiple"
126
+ v-bind="item.table"
127
+ v-else-if="item.searchType === 'table'"
128
+ />
129
+
120
130
  </n-search-item>
121
131
  </template>
122
132
  </template>
@@ -2,10 +2,11 @@
2
2
  <div class="n-search__item n-field-group">
3
3
 
4
4
  <!-- 比较1 -->
5
- <div class="n-field-group row">
5
+ <div class="n-field-group row no-wrap">
6
6
 
7
7
  <!-- 比较类型1 -->
8
8
  <q-select
9
+ class="n-field-group__select"
9
10
  v-model="modelValue[0].type"
10
11
  :options="compareOptions1"
11
12
  map-options
@@ -41,6 +42,7 @@
41
42
  >
42
43
  <!-- 比较类型2 -->
43
44
  <q-select
45
+ class="n-field-group__select"
44
46
  v-model="modelValue[1].type"
45
47
  :options="[
46
48
  { label: '<', value: dicts.SEARCH_TYPE__LT },
@@ -205,7 +207,12 @@ export default {
205
207
  // 第三个子节点
206
208
  &:nth-child(3) {
207
209
  .q-field__control {
210
+ height: 100% !important;
208
211
  background-color: rgba(var(--n-reverse-color-rgb), 0.04);
212
+
213
+ .q-field__marginal {
214
+ height: 100% !important;
215
+ }
209
216
  }
210
217
  }
211
218
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netang/quasar",
3
- "version": "0.0.24",
3
+ "version": "0.0.25",
4
4
  "description": "netang-quasar",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
package/utils/$search.js CHANGED
@@ -194,12 +194,13 @@ async function getOptions(rawSearchOptions, format) {
194
194
  newItem.select.options = await utils.runAsync(newItem.select.options)()
195
195
  }
196
196
 
197
- // 如果有树选项
197
+ // 如果有树选项(调用的是 <n-field-tree> 组件)
198
198
  } else if (_.has(newItem, 'tree')) {
199
199
  newItem.searchType = 'tree'
200
200
  newItem.tree = Object.assign({
201
201
  nodes: [],
202
202
  accordion: true,
203
+ clearable: true,
203
204
  }, newItem.tree)
204
205
 
205
206
  // 如果节点数组是方法
@@ -208,6 +209,20 @@ async function getOptions(rawSearchOptions, format) {
208
209
  newItem.tree.nodes = await utils.runAsync(newItem.tree.nodes)()
209
210
  }
210
211
 
212
+ // 如果有表格选项(调用的是 <n-field-table> 组件)
213
+ } else if (_.has(newItem, 'table')) {
214
+ newItem.searchType = 'table'
215
+ newItem.table = Object.assign({
216
+ // 值字段(必填)
217
+ valueKey: newItem.name,
218
+ // 是否可清除
219
+ clearable: true,
220
+ // 是否开启筛选
221
+ filter: true,
222
+ }, newItem.table)
223
+
224
+ console.log('111', newItem.table)
225
+
211
226
  // 否则为输入框
212
227
  } else {
213
228
  newItem.searchType = 'input'
package/utils/$table.js CHANGED
@@ -22,8 +22,8 @@ function create(params) {
22
22
  const $q = useQuasar()
23
23
 
24
24
  // 每页显示行数选项
25
- // const rowsPerPageOptions = [30, 40, 50, 100, 200, 500, 1000]
26
- const rowsPerPageOptions = [3, 40, 50, 100, 200, 500, 1000]
25
+ const rowsPerPageOptions = [30, 40, 50, 100, 200, 500, 1000]
26
+ // const rowsPerPageOptions = [3, 40, 50, 100, 200, 500, 1000]
27
27
 
28
28
  // 获取参数
29
29
  const o = _.merge({
@@ -556,8 +556,6 @@ function create(params) {
556
556
  }
557
557
  })
558
558
 
559
- console.log('newValue', newValue)
560
-
561
559
  // 还原表格搜索数据
562
560
  tableSearchValue.value = _.cloneDeep(newValue)
563
561
 
@@ -742,29 +740,27 @@ function create(params) {
742
740
  return
743
741
  }
744
742
 
745
- // 如果选择类型为单选
746
- if (o.selection === 'single') {
747
- tableSelected.value = [ row ]
748
-
749
- // 否则为多选
750
- } else {
743
+ const opt = {}
744
+ opt[o.rowKey] = row[o.rowKey]
751
745
 
752
- const opt = {}
753
- opt[o.rowKey] = row[o.rowKey]
746
+ // 获取当前数据索引
747
+ const itemIndex = _.findIndex(tableSelected.value, opt)
754
748
 
755
- // 获取当前数据索引
756
- const itemIndex = _.findIndex(tableSelected.value, opt)
749
+ // 如果不存在, 则添加
750
+ if (itemIndex === -1) {
757
751
 
758
- // 如果不存在
759
- if (itemIndex === -1) {
760
- // 则添加
761
- tableSelected.value.push(row)
752
+ // 如果选择类型为单选
753
+ if (o.selection === 'single') {
754
+ tableSelected.value = [ row ]
762
755
 
763
- // 否则
756
+ // 否则为多选
764
757
  } else {
765
- // 删除
766
- tableSelected.value.splice(itemIndex, 1)
758
+ tableSelected.value.push(row)
767
759
  }
760
+
761
+ // 否则删除
762
+ } else {
763
+ tableSelected.value.splice(itemIndex, 1)
768
764
  }
769
765
  }
770
766
 
@@ -898,7 +894,7 @@ function create(params) {
898
894
  }
899
895
 
900
896
  /**
901
- * 获取配置
897
+ * 获取表格配置
902
898
  */
903
899
  function config(routePath, path, defaultValue) {
904
900
  return _.get(tablesConfig, utils.slash(routePath, 'start', false) + (path ? '.' + path : ''), defaultValue)
@@ -910,6 +906,6 @@ function config(routePath, path, defaultValue) {
910
906
  utils.$table = {
911
907
  // 创建表格
912
908
  create,
913
- // 获取配置
909
+ // 获取表格配置
914
910
  config,
915
911
  }