@netang/quasar 0.2.13 → 0.2.15

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 (286) hide show
  1. package/.editorconfig +12 -12
  2. package/_docs/docs/components/field-table.md +58 -58
  3. package/_docs/docs/components/field-tree.md +21 -21
  4. package/_docs/docs/components/table.md +24 -24
  5. package/_docs/docs/utils/table.md +196 -196
  6. package/components/column-title/index.vue +38 -38
  7. package/components/data/index.vue +20 -20
  8. package/components/dialog/img-viewer/index.vue +697 -697
  9. package/components/dialog/index.vue +2 -0
  10. package/components/dragger/index.vue +203 -203
  11. package/components/drawer/index.vue +303 -303
  12. package/components/editor-code/index.vue +325 -325
  13. package/components/empty/index.vue +82 -80
  14. package/components/field-date/index.vue +850 -850
  15. package/components/field-date/methods.js +100 -100
  16. package/components/field-table/index.vue +1482 -1482
  17. package/components/field-text/index.vue +165 -165
  18. package/components/field-tree/index.vue +754 -754
  19. package/components/img/index.vue +237 -237
  20. package/components/input-number/index.vue +551 -551
  21. package/components/list-menu/index.vue +149 -149
  22. package/components/list-menu-item/index.vue +79 -79
  23. package/components/mixed-table/index.vue +532 -532
  24. package/components/mixed-table-splitter/index.vue +377 -377
  25. package/components/power-page/index.vue +96 -94
  26. package/components/price/index.vue +188 -188
  27. package/components/private/components/index.js +11 -11
  28. package/components/private/components/move-to-tree/index.vue +154 -154
  29. package/components/private/edit-power-data/index.vue +846 -846
  30. package/components/private/table-visible-columns-button/index.vue +114 -114
  31. package/components/render/index.vue +123 -123
  32. package/components/search/index.vue +231 -231
  33. package/components/search-item/index.vue +210 -210
  34. package/components/select/index.vue +177 -177
  35. package/components/splitter/index.vue +422 -422
  36. package/components/table/index.vue +513 -513
  37. package/components/table-column-fixed/index.vue +110 -110
  38. package/components/table-pagination/index.vue +192 -192
  39. package/components/table-summary/index.vue +107 -107
  40. package/components/thumbnail/index.vue +72 -72
  41. package/components/toolbar/index.vue +146 -146
  42. package/components/tree/index.vue +1728 -1728
  43. package/components/uploader/index.vue +195 -195
  44. package/components/uploader-query/index.vue +945 -945
  45. package/components/value-format/index.vue +274 -274
  46. package/configs/area3.js +1 -1
  47. package/docs/404.html +33 -33
  48. package/docs/assets/404.html-60b35caa.js +1 -1
  49. package/docs/assets/404.html-d1e63d77.js +1 -1
  50. package/docs/assets/alert.html-b2a2a72f.js +5 -5
  51. package/docs/assets/alert.html-ba46d137.js +1 -1
  52. package/docs/assets/app-9f30aa4b.js +6 -6
  53. package/docs/assets/area.html-01b9b58d.js +42 -42
  54. package/docs/assets/area.html-9a4fce6a.js +1 -1
  55. package/docs/assets/arr.html-145d27e7.js +1 -1
  56. package/docs/assets/arr.html-674e65ab.js +11 -11
  57. package/docs/assets/auth.html-579fa830.js +1 -1
  58. package/docs/assets/auth.html-8544ed95.js +8 -8
  59. package/docs/assets/bus.html-c71254aa.js +1 -1
  60. package/docs/assets/bus.html-dc7d3d19.js +6 -6
  61. package/docs/assets/column-title.html-c735cb5a.js +3 -3
  62. package/docs/assets/column-title.html-e9316762.js +1 -1
  63. package/docs/assets/confirm.html-ddfdc27f.js +10 -10
  64. package/docs/assets/confirm.html-ef3e2bef.js +1 -1
  65. package/docs/assets/copy.html-d20345b6.js +1 -1
  66. package/docs/assets/copy.html-ef8c8571.js +13 -13
  67. package/docs/assets/data.html-6432175d.js +30 -30
  68. package/docs/assets/data.html-a3b05d5b.js +1 -1
  69. package/docs/assets/dialog.html-1f698e5a.js +1 -1
  70. package/docs/assets/dialog.html-62902b83.js +68 -68
  71. package/docs/assets/dialog.html-baea77c9.js +1 -1
  72. package/docs/assets/dialog.html-bb082fc4.js +1 -1
  73. package/docs/assets/dict.html-1311da3d.js +23 -23
  74. package/docs/assets/dict.html-b96fbf0c.js +1 -1
  75. package/docs/assets/dictOptions.html-7c4f40a5.js +1 -1
  76. package/docs/assets/dictOptions.html-fb99d175.js +5 -5
  77. package/docs/assets/dragger.html-668d3efa.js +1 -1
  78. package/docs/assets/dragger.html-749d585a.js +1 -1
  79. package/docs/assets/editor-code.html-6ab26ea9.js +1 -1
  80. package/docs/assets/editor-code.html-d196205d.js +1 -1
  81. package/docs/assets/empty.html-1c139131.js +1 -1
  82. package/docs/assets/empty.html-1e9c441d.js +1 -1
  83. package/docs/assets/field-date.html-069fdb13.js +1 -1
  84. package/docs/assets/field-date.html-ad204aa9.js +1 -1
  85. package/docs/assets/field-table.html-ce480f03.js +1 -1
  86. package/docs/assets/field-table.html-d9236160.js +1 -1
  87. package/docs/assets/field-text.html-7277c62f.js +1 -1
  88. package/docs/assets/field-text.html-ccb4cecf.js +1 -1
  89. package/docs/assets/field-tree.html-519bfb45.js +1 -1
  90. package/docs/assets/field-tree.html-fdc748d6.js +1 -1
  91. package/docs/assets/form.html-2b562c37.js +2 -2
  92. package/docs/assets/form.html-75104cd5.js +1 -1
  93. package/docs/assets/framework-204010b2.js +5 -5
  94. package/docs/assets/getData.html-990e3787.js +1 -1
  95. package/docs/assets/getData.html-bb72025f.js +34 -34
  96. package/docs/assets/getFile.html-42368004.js +1 -1
  97. package/docs/assets/getFile.html-99abd054.js +3 -3
  98. package/docs/assets/getImage.html-3429c5a1.js +1 -1
  99. package/docs/assets/getImage.html-4d886d83.js +3 -3
  100. package/docs/assets/getTime.html-7435f922.js +1 -1
  101. package/docs/assets/getTime.html-b37f49eb.js +20 -20
  102. package/docs/assets/img.html-7d1da657.js +1 -1
  103. package/docs/assets/img.html-fbea1105.js +1 -1
  104. package/docs/assets/index.html-1695dd7c.js +1 -1
  105. package/docs/assets/index.html-65a4aa67.js +1 -1
  106. package/docs/assets/index.html-7b98d5bd.js +1 -1
  107. package/docs/assets/index.html-c01f2648.js +1 -1
  108. package/docs/assets/input-number.html-0b250d2a.js +1 -1
  109. package/docs/assets/input-number.html-a8eb0378.js +1 -1
  110. package/docs/assets/list-menu-item.html-7f1b4611.js +1 -1
  111. package/docs/assets/list-menu-item.html-84ed5ab8.js +1 -1
  112. package/docs/assets/list-menu.html-28b4163f.js +1 -1
  113. package/docs/assets/list-menu.html-cb6ba95b.js +1 -1
  114. package/docs/assets/loading.html-dae9e39d.js +6 -6
  115. package/docs/assets/loading.html-dc74c9e6.js +1 -1
  116. package/docs/assets/notify.html-e6c4c514.js +1 -1
  117. package/docs/assets/notify.html-f2c4d914.js +8 -8
  118. package/docs/assets/power-page.html-32e02f82.js +1 -1
  119. package/docs/assets/power-page.html-485e77da.js +1 -1
  120. package/docs/assets/power.html-d258cc19.js +93 -93
  121. package/docs/assets/power.html-e490bd32.js +1 -1
  122. package/docs/assets/previewImage.html-6a6b4245.js +1 -1
  123. package/docs/assets/previewImage.html-c5b7e945.js +2 -2
  124. package/docs/assets/price.html-1882c548.js +19 -19
  125. package/docs/assets/price.html-94d3f5be.js +1 -1
  126. package/docs/assets/price.html-d213df0f.js +1 -1
  127. package/docs/assets/price.html-deaf880f.js +1 -1
  128. package/docs/assets/render.html-8efcbdd4.js +1 -1
  129. package/docs/assets/render.html-df228e38.js +1 -1
  130. package/docs/assets/rule.html-2cd57fc2.js +13 -13
  131. package/docs/assets/rule.html-61662001.js +1 -1
  132. package/docs/assets/ruleValid.html-04fe2552.js +1 -1
  133. package/docs/assets/ruleValid.html-e0a776af.js +14 -14
  134. package/docs/assets/search-0782d0d1.svg +1 -1
  135. package/docs/assets/search-item.html-3f75394c.js +1 -1
  136. package/docs/assets/search-item.html-4e942ecd.js +1 -1
  137. package/docs/assets/search.html-2807043e.js +1 -1
  138. package/docs/assets/search.html-c24f8806.js +1 -1
  139. package/docs/assets/select.html-00d0607c.js +1 -1
  140. package/docs/assets/select.html-de7731f5.js +1 -1
  141. package/docs/assets/splitter.html-56f51a70.js +1 -1
  142. package/docs/assets/splitter.html-f5c836d7.js +1 -1
  143. package/docs/assets/style-161e43ab.css +1 -1
  144. package/docs/assets/symbols.html-a6aea4bf.js +1 -1
  145. package/docs/assets/symbols.html-b1f65bad.js +21 -21
  146. package/docs/assets/table-column-fixed.html-3a69e7b2.js +1 -1
  147. package/docs/assets/table-column-fixed.html-e763c38b.js +1 -1
  148. package/docs/assets/table-pagination.html-236934d3.js +1 -1
  149. package/docs/assets/table-pagination.html-c37ee2ac.js +1 -1
  150. package/docs/assets/table-splitter.html-07eab15c.js +1 -1
  151. package/docs/assets/table-splitter.html-7670ee65.js +1 -1
  152. package/docs/assets/table-summary.html-04db434f.js +1 -1
  153. package/docs/assets/table-summary.html-943c65a0.js +1 -1
  154. package/docs/assets/table.html-36253ad7.js +1 -1
  155. package/docs/assets/table.html-7f9c5d1b.js +38 -38
  156. package/docs/assets/table.html-93d53dc8.js +1 -1
  157. package/docs/assets/table.html-ac99b9cb.js +1 -1
  158. package/docs/assets/thumbnail.html-bab1976b.js +1 -1
  159. package/docs/assets/thumbnail.html-eb64e5e8.js +1 -1
  160. package/docs/assets/timestamp.html-4e54f79b.js +13 -13
  161. package/docs/assets/timestamp.html-d0e1b88a.js +1 -1
  162. package/docs/assets/toast.html-58ecbe21.js +1 -1
  163. package/docs/assets/toast.html-c9b9d36b.js +6 -6
  164. package/docs/assets/toolbar.html-83d9f97c.js +1 -1
  165. package/docs/assets/toolbar.html-ff7b8c92.js +1 -1
  166. package/docs/assets/tree.html-d07cbe79.js +23 -23
  167. package/docs/assets/tree.html-ea04193e.js +1 -1
  168. package/docs/assets/uploader-query.html-05590718.js +1 -1
  169. package/docs/assets/uploader-query.html-3175bac5.js +1 -1
  170. package/docs/assets/uploader.html-36da4394.js +2 -2
  171. package/docs/assets/uploader.html-6b5f3079.js +1 -1
  172. package/docs/assets/uploader.html-b9340b57.js +1 -1
  173. package/docs/assets/uploader.html-bc1c22e3.js +1 -1
  174. package/docs/assets/value-format.html-8ae3d47d.js +1 -1
  175. package/docs/assets/value-format.html-afa99b3d.js +1 -1
  176. package/docs/components/column-title.html +35 -35
  177. package/docs/components/data.html +62 -62
  178. package/docs/components/dialog.html +33 -33
  179. package/docs/components/dragger.html +33 -33
  180. package/docs/components/editor-code.html +33 -33
  181. package/docs/components/empty.html +33 -33
  182. package/docs/components/field-date.html +33 -33
  183. package/docs/components/field-table.html +33 -33
  184. package/docs/components/field-text.html +33 -33
  185. package/docs/components/field-tree.html +33 -33
  186. package/docs/components/img.html +33 -33
  187. package/docs/components/input-number.html +33 -33
  188. package/docs/components/list-menu-item.html +33 -33
  189. package/docs/components/list-menu.html +33 -33
  190. package/docs/components/power-page.html +33 -33
  191. package/docs/components/price.html +33 -33
  192. package/docs/components/render.html +33 -33
  193. package/docs/components/search-item.html +33 -33
  194. package/docs/components/search.html +33 -33
  195. package/docs/components/select.html +33 -33
  196. package/docs/components/splitter.html +33 -33
  197. package/docs/components/table-column-fixed.html +33 -33
  198. package/docs/components/table-pagination.html +33 -33
  199. package/docs/components/table-splitter.html +33 -33
  200. package/docs/components/table-summary.html +33 -33
  201. package/docs/components/table.html +33 -33
  202. package/docs/components/thumbnail.html +33 -33
  203. package/docs/components/toolbar.html +33 -33
  204. package/docs/components/uploader-query.html +33 -33
  205. package/docs/components/uploader.html +33 -33
  206. package/docs/components/value-format.html +33 -33
  207. package/docs/css/index.css +3 -3
  208. package/docs/index.html +33 -33
  209. package/docs/utils/alert.html +37 -37
  210. package/docs/utils/area.html +74 -74
  211. package/docs/utils/arr.html +43 -43
  212. package/docs/utils/auth.html +40 -40
  213. package/docs/utils/bus.html +38 -38
  214. package/docs/utils/confirm.html +42 -42
  215. package/docs/utils/copy.html +45 -45
  216. package/docs/utils/dialog.html +100 -100
  217. package/docs/utils/dict.html +55 -55
  218. package/docs/utils/dictOptions.html +37 -37
  219. package/docs/utils/form.html +34 -34
  220. package/docs/utils/getData.html +66 -66
  221. package/docs/utils/getFile.html +35 -35
  222. package/docs/utils/getImage.html +35 -35
  223. package/docs/utils/getTime.html +52 -52
  224. package/docs/utils/index.html +33 -33
  225. package/docs/utils/loading.html +38 -38
  226. package/docs/utils/notify.html +40 -40
  227. package/docs/utils/power.html +125 -125
  228. package/docs/utils/previewImage.html +34 -34
  229. package/docs/utils/price.html +51 -51
  230. package/docs/utils/rule.html +45 -45
  231. package/docs/utils/ruleValid.html +46 -46
  232. package/docs/utils/symbols.html +53 -53
  233. package/docs/utils/table.html +70 -70
  234. package/docs/utils/timestamp.html +45 -45
  235. package/docs/utils/toast.html +38 -38
  236. package/docs/utils/tree.html +55 -55
  237. package/docs/utils/uploader.html +34 -34
  238. package/package.json +1 -1
  239. package/sass/common.scss +184 -184
  240. package/sass/index.scss +12 -12
  241. package/sass/line.scss +39 -39
  242. package/sass/quasar/btn.scss +46 -46
  243. package/sass/quasar/common.scss +3 -3
  244. package/sass/quasar/drawer.scss +6 -6
  245. package/sass/quasar/field.scss +250 -250
  246. package/sass/quasar/loading.scss +6 -6
  247. package/sass/quasar/table.scss +168 -168
  248. package/sass/quasar/toolbar.scss +22 -22
  249. package/sass/variables.scss +140 -140
  250. package/store/index.js +29 -29
  251. package/utils/$auth.js +127 -127
  252. package/utils/$form.js +72 -72
  253. package/utils/$power.js +1464 -1464
  254. package/utils/$render.js +75 -75
  255. package/utils/$rule.js +13 -13
  256. package/utils/$ruleValid.js +10 -10
  257. package/utils/$search.js +416 -416
  258. package/utils/$table.js +1275 -1275
  259. package/utils/alert.js +12 -12
  260. package/utils/area.js +400 -400
  261. package/utils/arr.js +51 -51
  262. package/utils/bus.js +6 -6
  263. package/utils/config.js +60 -60
  264. package/utils/confirm.js +11 -11
  265. package/utils/copy.js +30 -30
  266. package/utils/dialog.js +36 -36
  267. package/utils/dict.js +21 -21
  268. package/utils/dictOptions.js +28 -28
  269. package/utils/getData.js +88 -88
  270. package/utils/getFile.js +67 -67
  271. package/utils/getImage.js +236 -236
  272. package/utils/getTime.js +113 -113
  273. package/utils/index.js +67 -67
  274. package/utils/loading.js +15 -15
  275. package/utils/notify.js +13 -13
  276. package/utils/play.js +40 -40
  277. package/utils/previewImage.js +14 -14
  278. package/utils/price.js +18 -18
  279. package/utils/symbols.js +18 -18
  280. package/utils/timestamp.js +18 -18
  281. package/utils/toast.js +13 -13
  282. package/utils/uploader.js +2099 -2099
  283. package/utils/useAuth.js +30 -30
  284. package/utils/useFileUrl.js +26 -26
  285. package/utils/useRouter.js +47 -47
  286. package/utils/useSearch.js +499 -499
@@ -1,754 +1,754 @@
1
- <template>
2
- <q-field
3
- class="n-field-tree"
4
- :model-value="showValue"
5
- :disable="disable"
6
- :readonly="readonly"
7
- :clearable="clearable"
8
- @focus="onFieldFocus"
9
- @blur="onFieldBlur"
10
- @clear="onFieldClear"
11
- v-bind="$attrs"
12
- >
13
- <template v-slot:control>
14
-
15
- <template v-if="multiple">
16
-
17
- <template v-if="treeTickedNodes.length">
18
-
19
- <!-- 多选插槽 -->
20
- <slot
21
- name="selected"
22
- :ticked="treeTickedNodes"
23
- :remove="onRemoveItem"
24
- v-if="$slots.ticked"
25
- />
26
-
27
- <!-- 显示折叠的值数量 -->
28
- <q-chip
29
- dense
30
- :label="`+${treeTickedNodes.length}`"
31
- v-else-if="collapseTags"
32
- />
33
-
34
- <!-- 多选标签 -->
35
- <template v-else>
36
- <q-chip
37
- v-for="(item, index) in treeTickedNodes"
38
- :key="`item-${index}`"
39
- :label="item.label"
40
- dense
41
- :removable="! readonly && ! disable"
42
- @remove="onRemoveItem(index)"
43
- />
44
- </template>
45
- </template>
46
-
47
- <!-- 占位符-->
48
- <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
49
- </template>
50
-
51
- <!-- 显示文字 -->
52
- <span v-else-if="showValue">{{showValue}}</span>
53
-
54
- <!-- 占位符-->
55
- <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
56
-
57
- <!-- 筛选输入框 -->
58
- <input
59
- ref="inputRef"
60
- class="q-field__input q-placeholder col q-field__input--padding"
61
- v-model="inputValue"
62
- v-if="filter"
63
- />
64
- </template>
65
-
66
- <template v-slot:before v-if="$slots.before">
67
- <slot name="before" />
68
- </template>
69
- <template v-slot:prepend v-if="$slots.prepend">
70
- <slot name="prepend" />
71
- </template>
72
- <template v-slot:append v-if="$slots.append">
73
- <slot name="append" />
74
- </template>
75
- <template v-slot:after v-if="$slots.after">
76
- <slot name="after" />
77
- </template>
78
-
79
- <!-- 弹出层代理 -->
80
- <q-popup-proxy
81
- ref="popupRef"
82
- no-refocus
83
- no-focus
84
- fit
85
- @focus="onFieldFocus"
86
- @show="onPopupShow"
87
- @before-hide="showPopup = false"
88
- v-if="! readonly && ! disable"
89
- >
90
- <q-card>
91
- <!-- 树 -->
92
- <n-tree
93
- ref="treeRef"
94
- style="min-width:260px;max-height:380px;"
95
- :filter="inputValue"
96
- :nodes="currentTreeNodes"
97
- :node-key="nodeKey"
98
- :label-key="labelKey"
99
- :ticked="treeTicked"
100
- @update:ticked="emitModelValue"
101
- v-model:expanded="treeExpanded"
102
- :tick-strategy="multiple ? (strict ? 'strict' : 'leaf') : 'none'"
103
- :accordion="accordion"
104
- v-bind="treeProps"
105
- v-if="showTree"
106
- >
107
- <template v-slot:default-header="{ node }">
108
- <div
109
- :class="{
110
- 'text-primary': checkTreeNodeActive(node),
111
- }"
112
- @click="onNode($event, node)"
113
- >{{node.label}}</div>
114
- </template>
115
- </n-tree>
116
-
117
- <!-- loading -->
118
- <div class="flex flex-center" style="height:100px" v-else>
119
- <q-spinner
120
- color="primary"
121
- size="3em"
122
- />
123
- </div>
124
-
125
- </q-card>
126
- </q-popup-proxy>
127
- </q-field>
128
- </template>
129
-
130
- <script>
131
- import { ref, computed, watch, onUpdated, nextTick } from 'vue'
132
-
133
- import $n_has from 'lodash/has'
134
- import $n_uniq from 'lodash/uniq'
135
- import $n_concat from 'lodash/concat'
136
- import $n_isFunction from 'lodash/isFunction'
137
-
138
- import $n_indexOf from '@netang/utils/indexOf'
139
- import $n_isRequired from '@netang/utils/isRequired'
140
- import $n_isValidArray from '@netang/utils/isValidArray'
141
- import $n_isValidValue from '@netang/utils/isValidValue'
142
- import $n_isValidObject from '@netang/utils/isValidObject'
143
- import $n_runAsync from '@netang/utils/runAsync'
144
-
145
- export default {
146
-
147
- /**
148
- * 标识
149
- */
150
- name: 'NFieldTree',
151
-
152
- /**
153
- * 声明属性
154
- */
155
- props: {
156
- // 值 v-model
157
- modelValue: {
158
- required: true,
159
- },
160
- // 树展开节点
161
- expanded: Array, // v-model:expanded
162
- // 节点数组
163
- // Array: 初始节点数据数据组
164
- // Function: 获取初始节点数据的方法
165
- nodes: [ Array, Function ],
166
- // 唯一的节点键值
167
- nodeKey: {
168
- type: String,
169
- default: 'id',
170
- },
171
- // 标签字段
172
- labelKey: {
173
- type: String,
174
- default: 'label',
175
- },
176
- // 是否可选任意一级(true:可选任意一级, false: 仅能选叶子节点)
177
- strict: Boolean,
178
- // 是否多选
179
- multiple: Boolean,
180
- // 手风琴模式
181
- accordion: Boolean,
182
- // 是否显示选中值的完整路径
183
- showAllLevels: {
184
- type: Boolean,
185
- default: true,
186
- },
187
- // 是否开启筛选
188
- filter: Boolean,
189
- // 是否折叠标签(多选模式有效)
190
- collapseTags: Boolean,
191
- // 占位符
192
- placeholder: String,
193
- // 是否可清除
194
- clearable: Boolean,
195
- // 是否禁用
196
- disable: Boolean,
197
- // 是否只读
198
- readonly: Boolean,
199
- // 树声明属性
200
- treeProps: Object,
201
- },
202
-
203
- /**
204
- * 声明事件
205
- */
206
- emits: [
207
- 'update:modelValue',
208
- ],
209
-
210
- /**
211
- * 组合式
212
- */
213
- setup(props, { emit }) {
214
-
215
- // ==========【数据】============================================================================================
216
-
217
- // 是否为初始加载树节点树
218
- const isDefaultLoadNodes = $n_isFunction(props.nodes)
219
-
220
- // 树节点是否已加载
221
- let __treeNodesLoaded = false
222
-
223
- // 输入框节点
224
- const inputRef = ref(null)
225
-
226
- // 输入框值
227
- const inputValue = ref('')
228
-
229
- // 弹出层节点
230
- const popupRef = ref(null)
231
-
232
- // 是否显示弹出层
233
- const showPopup = ref(false)
234
-
235
- // 是否显示树(如果为非初始加载树节点树, 则直接显示)
236
- const showTree = ref(! isDefaultLoadNodes)
237
-
238
- // 树节点
239
- const treeRef = ref(null)
240
-
241
- // 当前树节点数据
242
- const currentTreeNodes = ref(isDefaultLoadNodes ? [] : props.nodes)
243
-
244
- // tree all
245
- const treeAll = ref(getTreeAll())
246
-
247
- // 树展开数据
248
- const treeExpanded = ref(getExpanded())
249
-
250
- // 树选择数据
251
- const treeTicked = ref(formatModelValue(props.modelValue))
252
-
253
- // 如果为初始加载树节点树
254
- if (isDefaultLoadNodes) {
255
-
256
- // 初始加载节点
257
- defaultLoadNodes()
258
- .finally()
259
- }
260
-
261
- // ==========【计算属性】=========================================================================================
262
-
263
- /**
264
- * 树选择节点数据
265
- */
266
- const treeTickedNodes = computed(function () {
267
-
268
- const lists = []
269
-
270
- const hasTreeAll = $n_isValidObject(treeAll.value)
271
-
272
- for (const treeKey of treeTicked.value) {
273
- lists.push({
274
- id: treeKey,
275
- label: hasTreeAll && $n_has(treeAll.value, treeKey) ?
276
- treeAll.value[treeKey][props.showAllLevels ? 'path' : 'label']
277
- : treeKey
278
- })
279
- }
280
-
281
- return lists
282
- })
283
-
284
- /**
285
- * 显示值
286
- */
287
- const showValue = computed(function () {
288
-
289
- // 如果有已选数据
290
- return $n_isValidArray(treeTickedNodes.value)
291
- // 取已选数据第一条的标签
292
- ? treeTickedNodes.value[0].label
293
- : ''
294
- })
295
-
296
- // ==========【监听数据】=========================================================================================
297
-
298
- /**
299
- * 监听声明节点
300
- */
301
- watch(() => props.nodes, defaultLoadNodes)
302
-
303
- /**
304
- * 监听声明值
305
- */
306
- watch(() => props.modelValue, function(val) {
307
-
308
- // 设置选中数据
309
- treeTicked.value = formatModelValue(val)
310
-
311
- // 设置输入框焦点
312
- setInputFocus()
313
-
314
- // 设置输入框文字选中
315
- setInputSelection()
316
- })
317
-
318
- /**
319
- * 监听输入框值
320
- */
321
- watch(inputValue, function (val) {
322
- if (
323
- // 如果弹出层是隐藏的
324
- ! showPopup.value
325
- // 如果输入框有值
326
- && $n_isValidValue(val)
327
- ) {
328
- // 显示弹出层
329
- popupRef.value.show()
330
- }
331
- })
332
-
333
- // ==========【方法】=============================================================================================
334
-
335
- /**
336
- * 获取树子节点
337
- */
338
- function _getTreeChildren(all, data, pid, pPath) {
339
- for (const item of data) {
340
-
341
- const label = item[props.labelKey]
342
-
343
- const path = pPath ? (pPath + ' / ' + label) : label
344
-
345
- all[item[props.nodeKey]] = {
346
- id: item[props.nodeKey],
347
- pid,
348
- label,
349
- children: item.children,
350
- path,
351
- }
352
-
353
- // 如果是父节点
354
- if ($n_isValidArray(item.children)) {
355
- _getTreeChildren(all, item.children, item.id, path)
356
- }
357
- }
358
- }
359
-
360
- /**
361
- * 获取 tree all
362
- */
363
- function getTreeAll() {
364
-
365
- const all = {}
366
-
367
- // 如果当前树节点数据为有效数组
368
- if ($n_isValidArray(currentTreeNodes.value)) {
369
- _getTreeChildren(all, currentTreeNodes.value, 0, '')
370
- }
371
-
372
- return all
373
- }
374
-
375
- /**
376
- * 获取展开节点数组
377
- */
378
- function getExpanded() {
379
-
380
- let expanded = []
381
-
382
- if (
383
- // 如果是单选
384
- ! props.multiple
385
- // 如果有值
386
- && $n_isRequired(props.modelValue)
387
- // 如果有 tree all
388
- && $n_isValidObject(treeAll.value)
389
- // 存在节点
390
- && $n_has(treeAll.value, props.modelValue)
391
- ) {
392
- // 获取父节点
393
- function getParent({ id, pid, children }) {
394
-
395
- // 如果是父级节点
396
- if ($n_isValidArray(children)) {
397
- // 设为展开
398
- expanded.push(id)
399
- }
400
-
401
- // 如果有父节点, 则继续向上寻找
402
- if (pid && $n_has(treeAll.value, pid)) {
403
- getParent(treeAll.value[pid])
404
- }
405
- }
406
-
407
- getParent(treeAll.value[props.modelValue])
408
-
409
- if (props.expanded) {
410
- expanded = $n_uniq($n_concat(expanded, props.expanded))
411
- }
412
- }
413
-
414
- return expanded
415
- }
416
-
417
- /**
418
- * 格式化传值
419
- */
420
- function formatModelValue(val) {
421
-
422
- // 如果是多选
423
- if (props.multiple) {
424
- return $n_isValidArray(val) ? val : []
425
- }
426
-
427
- // 如果为有效值
428
- if ($n_isRequired(val)) {
429
- return [ val ]
430
- }
431
-
432
- return []
433
- }
434
-
435
- /**
436
- * 触发更新值
437
- */
438
- function emitModelValue(val) {
439
-
440
- // 触发更新值
441
- emit('update:modelValue', val)
442
- }
443
-
444
- /**
445
- * 点击节点
446
- */
447
- function onNode(e, { id, children }) {
448
-
449
- // 如果是多选
450
- if (props.multiple) {
451
-
452
- // 如果是父节点
453
- if ($n_isValidArray(children)) {
454
-
455
- // 则无任何操作
456
- return
457
- }
458
-
459
- // 克隆已选树数据
460
- const _ticked = [...treeTicked.value]
461
-
462
- // 获取值在树数据中的索引
463
- const index = $n_indexOf(_ticked, id)
464
-
465
- // 如果在数据中
466
- if (index > -1) {
467
- // 则删除
468
- _ticked.splice(index, 1)
469
- // 否则
470
- } else {
471
- // 添加
472
- _ticked.push(id)
473
- }
474
-
475
- // 触发更新值
476
- // 设置树选择数据
477
- emitModelValue(_ticked)
478
-
479
- // 否则是单选
480
- } else {
481
-
482
- if (
483
- // 如果是父节点
484
- $n_isValidArray(children)
485
- // 如果仅可选择叶子节点
486
- && ! props.strict
487
- ) {
488
- // 则无任何操作
489
- return
490
- }
491
-
492
- // 触发更新值
493
- // 设置树选择数据
494
- emitModelValue(id)
495
-
496
- // 则停止冒泡
497
- e.preventDefault()
498
- e.stopPropagation()
499
-
500
- // 则关闭弹出层
501
- popupRef.value.hide()
502
- }
503
- }
504
-
505
- /**
506
- * 移除单个节点
507
- */
508
- function onRemoveItem(index) {
509
-
510
- // 克隆已选树数据
511
- const _ticked = [...treeTicked.value]
512
-
513
- // 删除该节点
514
- _ticked.splice(index, 1)
515
-
516
- // 触发更新值
517
- // 设置树选择数据
518
- emitModelValue(_ticked)
519
- }
520
-
521
- /**
522
- * 字段获取焦点触发
523
- */
524
- function onFieldFocus(e) {
525
-
526
- // 停止冒泡
527
- e.stopPropagation()
528
-
529
- // 设置输入框焦点
530
- setInputFocus()
531
-
532
- // window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0)
533
- }
534
-
535
- /**
536
- * 字段失去焦点触发
537
- */
538
- function onFieldBlur(e) {
539
-
540
- // 停止冒泡
541
- e.stopPropagation()
542
-
543
- if (
544
- // 如果开启筛选
545
- props.filter
546
- // 如果没有显示弹出层
547
- && ! showPopup.value
548
- ) {
549
- // 清空输入框值
550
- inputValue.value = ''
551
- }
552
- }
553
-
554
- /**
555
- * 字段清空触发
556
- */
557
- function onFieldClear() {
558
-
559
- // 触发更新值
560
- // 清空树数据
561
- emitModelValue(props.multiple ? [] : null)
562
-
563
- // 隐藏弹出层
564
- popupRef.value.hide()
565
- }
566
-
567
- /**
568
- * 弹出层显示回调
569
- */
570
- async function onPopupShow() {
571
-
572
- // 显示弹出层
573
- showPopup.value = true
574
-
575
- // 设置输入框焦点
576
- setInputFocus()
577
-
578
- if (
579
- // 如果树已加载过了
580
- __treeNodesLoaded
581
- // 如果树已显示
582
- || showTree.value
583
- ) {
584
- // 则无任何操作
585
- return
586
- }
587
-
588
- // 初始加载节点
589
- await defaultLoadNodes()
590
- }
591
-
592
- /**
593
- * 设置输入框文字选中
594
- */
595
- function setInputSelection() {
596
- if (
597
- // 如果开启筛选
598
- props.filter
599
- // 如果有输入框节点
600
- && inputRef.value
601
- // 如果输入框有值
602
- && inputValue.value.length
603
- ) {
604
- // 全选文字
605
- inputRef.value.select()
606
- // inputRef.value.setSelectionRange(0, inputValue.value.length)
607
- }
608
- }
609
-
610
- /**
611
- * 设置输入框焦点
612
- */
613
- function setInputFocus() {
614
- if (
615
- // 如果开启筛选
616
- props.filter
617
- // 如果有输入框节点
618
- && inputRef.value
619
- ) {
620
- inputRef.value.focus()
621
- }
622
- }
623
-
624
- /**
625
- * 树节点是否激活
626
- */
627
- function checkTreeNodeActive({ id }) {
628
- return $n_indexOf(treeTicked.value, id) > -1
629
- }
630
-
631
- /**
632
- * 初始加载节点
633
- */
634
- async function defaultLoadNodes() {
635
-
636
- // 如果是初始加载树节点树
637
- if ($n_isFunction(props.nodes)) {
638
-
639
- // 隐藏树
640
- showTree.value = false
641
-
642
- // 下次 DOM 更新
643
- await nextTick()
644
-
645
- // 通过自定义方法获取树节点数组
646
- const resNodes = await $n_runAsync(props.nodes)()
647
-
648
- if ($n_isValidArray(resNodes)) {
649
-
650
- // 设置当前树节点数组
651
- currentTreeNodes.value = resNodes
652
-
653
- // 设置 tree all
654
- treeAll.value = getTreeAll()
655
-
656
- // 设置开数据
657
- treeExpanded.value = getExpanded()
658
-
659
- } else {
660
-
661
- // 设置当前树节点数组
662
- currentTreeNodes.value = []
663
-
664
- // 设置 tree all
665
- treeAll.value = getTreeAll()
666
- }
667
-
668
- // 否则为节点数组数据
669
- } else {
670
-
671
- // 设置当前树节点数组
672
- currentTreeNodes.value = $n_isValidArray(props.nodes) ? props.nodes : []
673
-
674
- // 设置 tree all
675
- treeAll.value = getTreeAll()
676
- }
677
-
678
- // 设置显示树
679
- showTree.value = true
680
-
681
- // 树已加载
682
- __treeNodesLoaded = true
683
- }
684
-
685
- // ==========【生命周期】=========================================================================================
686
-
687
- /**
688
- * 在组件因为响应式状态变更而更新其 DOM 树之后调用
689
- */
690
- onUpdated(function () {
691
- if ($n_has(popupRef.value, 'currentComponent.ref.updatePosition')) {
692
- popupRef.value.currentComponent.ref.updatePosition()
693
- }
694
- })
695
-
696
- // ==========【返回】=============================================================================================
697
-
698
- return {
699
- // 显示值
700
- showValue,
701
- // 输入框节点
702
- inputRef,
703
- // 输入框值
704
- inputValue,
705
- // 弹出层节点
706
- popupRef,
707
- // 是否显示弹出层
708
- showPopup,
709
- // 是否显示树
710
- showTree,
711
- // 树节点
712
- treeRef,
713
- // 树选择数据
714
- treeTicked,
715
- // 树选择节点数据
716
- treeTickedNodes,
717
- // 树展开数据
718
- treeExpanded,
719
- // 当前树节点数据
720
- currentTreeNodes,
721
-
722
- // 触发更新值
723
- emitModelValue,
724
- // 点击节点
725
- onNode,
726
- // 移除单个
727
- onRemoveItem,
728
-
729
- // 字段获取焦点触发
730
- onFieldFocus,
731
- // 字段失去焦点触发
732
- onFieldBlur,
733
- // 字段清空触发
734
- onFieldClear,
735
-
736
- // 弹出层显示回调
737
- onPopupShow,
738
-
739
- // 节点是否激活
740
- checkTreeNodeActive,
741
- }
742
- },
743
- }
744
- </script>
745
-
746
- <style lang="scss">
747
- .n-field-tree {
748
- .q-field__input--padding {
749
- padding-left: 4px;
750
- min-width: 50px !important;
751
- cursor: text;
752
- }
753
- }
754
- </style>
1
+ <template>
2
+ <q-field
3
+ class="n-field-tree"
4
+ :model-value="showValue"
5
+ :disable="disable"
6
+ :readonly="readonly"
7
+ :clearable="clearable"
8
+ @focus="onFieldFocus"
9
+ @blur="onFieldBlur"
10
+ @clear="onFieldClear"
11
+ v-bind="$attrs"
12
+ >
13
+ <template v-slot:control>
14
+
15
+ <template v-if="multiple">
16
+
17
+ <template v-if="treeTickedNodes.length">
18
+
19
+ <!-- 多选插槽 -->
20
+ <slot
21
+ name="selected"
22
+ :ticked="treeTickedNodes"
23
+ :remove="onRemoveItem"
24
+ v-if="$slots.ticked"
25
+ />
26
+
27
+ <!-- 显示折叠的值数量 -->
28
+ <q-chip
29
+ dense
30
+ :label="`+${treeTickedNodes.length}`"
31
+ v-else-if="collapseTags"
32
+ />
33
+
34
+ <!-- 多选标签 -->
35
+ <template v-else>
36
+ <q-chip
37
+ v-for="(item, index) in treeTickedNodes"
38
+ :key="`item-${index}`"
39
+ :label="item.label"
40
+ dense
41
+ :removable="! readonly && ! disable"
42
+ @remove="onRemoveItem(index)"
43
+ />
44
+ </template>
45
+ </template>
46
+
47
+ <!-- 占位符-->
48
+ <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
49
+ </template>
50
+
51
+ <!-- 显示文字 -->
52
+ <span v-else-if="showValue">{{showValue}}</span>
53
+
54
+ <!-- 占位符-->
55
+ <span class="n-placeholder" v-else-if="placeholder">{{placeholder}}</span>
56
+
57
+ <!-- 筛选输入框 -->
58
+ <input
59
+ ref="inputRef"
60
+ class="q-field__input q-placeholder col q-field__input--padding"
61
+ v-model="inputValue"
62
+ v-if="filter"
63
+ />
64
+ </template>
65
+
66
+ <template v-slot:before v-if="$slots.before">
67
+ <slot name="before" />
68
+ </template>
69
+ <template v-slot:prepend v-if="$slots.prepend">
70
+ <slot name="prepend" />
71
+ </template>
72
+ <template v-slot:append v-if="$slots.append">
73
+ <slot name="append" />
74
+ </template>
75
+ <template v-slot:after v-if="$slots.after">
76
+ <slot name="after" />
77
+ </template>
78
+
79
+ <!-- 弹出层代理 -->
80
+ <q-popup-proxy
81
+ ref="popupRef"
82
+ no-refocus
83
+ no-focus
84
+ fit
85
+ @focus="onFieldFocus"
86
+ @show="onPopupShow"
87
+ @before-hide="showPopup = false"
88
+ v-if="! readonly && ! disable"
89
+ >
90
+ <q-card>
91
+ <!-- 树 -->
92
+ <n-tree
93
+ ref="treeRef"
94
+ style="min-width:260px;max-height:380px;"
95
+ :filter="inputValue"
96
+ :nodes="currentTreeNodes"
97
+ :node-key="nodeKey"
98
+ :label-key="labelKey"
99
+ :ticked="treeTicked"
100
+ @update:ticked="emitModelValue"
101
+ v-model:expanded="treeExpanded"
102
+ :tick-strategy="multiple ? (strict ? 'strict' : 'leaf') : 'none'"
103
+ :accordion="accordion"
104
+ v-bind="treeProps"
105
+ v-if="showTree"
106
+ >
107
+ <template v-slot:default-header="{ node }">
108
+ <div
109
+ :class="{
110
+ 'text-primary': checkTreeNodeActive(node),
111
+ }"
112
+ @click="onNode($event, node)"
113
+ >{{node.label}}</div>
114
+ </template>
115
+ </n-tree>
116
+
117
+ <!-- loading -->
118
+ <div class="flex flex-center" style="height:100px" v-else>
119
+ <q-spinner
120
+ color="primary"
121
+ size="3em"
122
+ />
123
+ </div>
124
+
125
+ </q-card>
126
+ </q-popup-proxy>
127
+ </q-field>
128
+ </template>
129
+
130
+ <script>
131
+ import { ref, computed, watch, onUpdated, nextTick } from 'vue'
132
+
133
+ import $n_has from 'lodash/has'
134
+ import $n_uniq from 'lodash/uniq'
135
+ import $n_concat from 'lodash/concat'
136
+ import $n_isFunction from 'lodash/isFunction'
137
+
138
+ import $n_indexOf from '@netang/utils/indexOf'
139
+ import $n_isRequired from '@netang/utils/isRequired'
140
+ import $n_isValidArray from '@netang/utils/isValidArray'
141
+ import $n_isValidValue from '@netang/utils/isValidValue'
142
+ import $n_isValidObject from '@netang/utils/isValidObject'
143
+ import $n_runAsync from '@netang/utils/runAsync'
144
+
145
+ export default {
146
+
147
+ /**
148
+ * 标识
149
+ */
150
+ name: 'NFieldTree',
151
+
152
+ /**
153
+ * 声明属性
154
+ */
155
+ props: {
156
+ // 值 v-model
157
+ modelValue: {
158
+ required: true,
159
+ },
160
+ // 树展开节点
161
+ expanded: Array, // v-model:expanded
162
+ // 节点数组
163
+ // Array: 初始节点数据数据组
164
+ // Function: 获取初始节点数据的方法
165
+ nodes: [ Array, Function ],
166
+ // 唯一的节点键值
167
+ nodeKey: {
168
+ type: String,
169
+ default: 'id',
170
+ },
171
+ // 标签字段
172
+ labelKey: {
173
+ type: String,
174
+ default: 'label',
175
+ },
176
+ // 是否可选任意一级(true:可选任意一级, false: 仅能选叶子节点)
177
+ strict: Boolean,
178
+ // 是否多选
179
+ multiple: Boolean,
180
+ // 手风琴模式
181
+ accordion: Boolean,
182
+ // 是否显示选中值的完整路径
183
+ showAllLevels: {
184
+ type: Boolean,
185
+ default: true,
186
+ },
187
+ // 是否开启筛选
188
+ filter: Boolean,
189
+ // 是否折叠标签(多选模式有效)
190
+ collapseTags: Boolean,
191
+ // 占位符
192
+ placeholder: String,
193
+ // 是否可清除
194
+ clearable: Boolean,
195
+ // 是否禁用
196
+ disable: Boolean,
197
+ // 是否只读
198
+ readonly: Boolean,
199
+ // 树声明属性
200
+ treeProps: Object,
201
+ },
202
+
203
+ /**
204
+ * 声明事件
205
+ */
206
+ emits: [
207
+ 'update:modelValue',
208
+ ],
209
+
210
+ /**
211
+ * 组合式
212
+ */
213
+ setup(props, { emit }) {
214
+
215
+ // ==========【数据】============================================================================================
216
+
217
+ // 是否为初始加载树节点树
218
+ const isDefaultLoadNodes = $n_isFunction(props.nodes)
219
+
220
+ // 树节点是否已加载
221
+ let __treeNodesLoaded = false
222
+
223
+ // 输入框节点
224
+ const inputRef = ref(null)
225
+
226
+ // 输入框值
227
+ const inputValue = ref('')
228
+
229
+ // 弹出层节点
230
+ const popupRef = ref(null)
231
+
232
+ // 是否显示弹出层
233
+ const showPopup = ref(false)
234
+
235
+ // 是否显示树(如果为非初始加载树节点树, 则直接显示)
236
+ const showTree = ref(! isDefaultLoadNodes)
237
+
238
+ // 树节点
239
+ const treeRef = ref(null)
240
+
241
+ // 当前树节点数据
242
+ const currentTreeNodes = ref(isDefaultLoadNodes ? [] : props.nodes)
243
+
244
+ // tree all
245
+ const treeAll = ref(getTreeAll())
246
+
247
+ // 树展开数据
248
+ const treeExpanded = ref(getExpanded())
249
+
250
+ // 树选择数据
251
+ const treeTicked = ref(formatModelValue(props.modelValue))
252
+
253
+ // 如果为初始加载树节点树
254
+ if (isDefaultLoadNodes) {
255
+
256
+ // 初始加载节点
257
+ defaultLoadNodes()
258
+ .finally()
259
+ }
260
+
261
+ // ==========【计算属性】=========================================================================================
262
+
263
+ /**
264
+ * 树选择节点数据
265
+ */
266
+ const treeTickedNodes = computed(function () {
267
+
268
+ const lists = []
269
+
270
+ const hasTreeAll = $n_isValidObject(treeAll.value)
271
+
272
+ for (const treeKey of treeTicked.value) {
273
+ lists.push({
274
+ id: treeKey,
275
+ label: hasTreeAll && $n_has(treeAll.value, treeKey) ?
276
+ treeAll.value[treeKey][props.showAllLevels ? 'path' : 'label']
277
+ : treeKey
278
+ })
279
+ }
280
+
281
+ return lists
282
+ })
283
+
284
+ /**
285
+ * 显示值
286
+ */
287
+ const showValue = computed(function () {
288
+
289
+ // 如果有已选数据
290
+ return $n_isValidArray(treeTickedNodes.value)
291
+ // 取已选数据第一条的标签
292
+ ? treeTickedNodes.value[0].label
293
+ : ''
294
+ })
295
+
296
+ // ==========【监听数据】=========================================================================================
297
+
298
+ /**
299
+ * 监听声明节点
300
+ */
301
+ watch(() => props.nodes, defaultLoadNodes)
302
+
303
+ /**
304
+ * 监听声明值
305
+ */
306
+ watch(() => props.modelValue, function(val) {
307
+
308
+ // 设置选中数据
309
+ treeTicked.value = formatModelValue(val)
310
+
311
+ // 设置输入框焦点
312
+ setInputFocus()
313
+
314
+ // 设置输入框文字选中
315
+ setInputSelection()
316
+ })
317
+
318
+ /**
319
+ * 监听输入框值
320
+ */
321
+ watch(inputValue, function (val) {
322
+ if (
323
+ // 如果弹出层是隐藏的
324
+ ! showPopup.value
325
+ // 如果输入框有值
326
+ && $n_isValidValue(val)
327
+ ) {
328
+ // 显示弹出层
329
+ popupRef.value.show()
330
+ }
331
+ })
332
+
333
+ // ==========【方法】=============================================================================================
334
+
335
+ /**
336
+ * 获取树子节点
337
+ */
338
+ function _getTreeChildren(all, data, pid, pPath) {
339
+ for (const item of data) {
340
+
341
+ const label = item[props.labelKey]
342
+
343
+ const path = pPath ? (pPath + ' / ' + label) : label
344
+
345
+ all[item[props.nodeKey]] = {
346
+ id: item[props.nodeKey],
347
+ pid,
348
+ label,
349
+ children: item.children,
350
+ path,
351
+ }
352
+
353
+ // 如果是父节点
354
+ if ($n_isValidArray(item.children)) {
355
+ _getTreeChildren(all, item.children, item.id, path)
356
+ }
357
+ }
358
+ }
359
+
360
+ /**
361
+ * 获取 tree all
362
+ */
363
+ function getTreeAll() {
364
+
365
+ const all = {}
366
+
367
+ // 如果当前树节点数据为有效数组
368
+ if ($n_isValidArray(currentTreeNodes.value)) {
369
+ _getTreeChildren(all, currentTreeNodes.value, 0, '')
370
+ }
371
+
372
+ return all
373
+ }
374
+
375
+ /**
376
+ * 获取展开节点数组
377
+ */
378
+ function getExpanded() {
379
+
380
+ let expanded = []
381
+
382
+ if (
383
+ // 如果是单选
384
+ ! props.multiple
385
+ // 如果有值
386
+ && $n_isRequired(props.modelValue)
387
+ // 如果有 tree all
388
+ && $n_isValidObject(treeAll.value)
389
+ // 存在节点
390
+ && $n_has(treeAll.value, props.modelValue)
391
+ ) {
392
+ // 获取父节点
393
+ function getParent({ id, pid, children }) {
394
+
395
+ // 如果是父级节点
396
+ if ($n_isValidArray(children)) {
397
+ // 设为展开
398
+ expanded.push(id)
399
+ }
400
+
401
+ // 如果有父节点, 则继续向上寻找
402
+ if (pid && $n_has(treeAll.value, pid)) {
403
+ getParent(treeAll.value[pid])
404
+ }
405
+ }
406
+
407
+ getParent(treeAll.value[props.modelValue])
408
+
409
+ if (props.expanded) {
410
+ expanded = $n_uniq($n_concat(expanded, props.expanded))
411
+ }
412
+ }
413
+
414
+ return expanded
415
+ }
416
+
417
+ /**
418
+ * 格式化传值
419
+ */
420
+ function formatModelValue(val) {
421
+
422
+ // 如果是多选
423
+ if (props.multiple) {
424
+ return $n_isValidArray(val) ? val : []
425
+ }
426
+
427
+ // 如果为有效值
428
+ if ($n_isRequired(val)) {
429
+ return [ val ]
430
+ }
431
+
432
+ return []
433
+ }
434
+
435
+ /**
436
+ * 触发更新值
437
+ */
438
+ function emitModelValue(val) {
439
+
440
+ // 触发更新值
441
+ emit('update:modelValue', val)
442
+ }
443
+
444
+ /**
445
+ * 点击节点
446
+ */
447
+ function onNode(e, { id, children }) {
448
+
449
+ // 如果是多选
450
+ if (props.multiple) {
451
+
452
+ // 如果是父节点
453
+ if ($n_isValidArray(children)) {
454
+
455
+ // 则无任何操作
456
+ return
457
+ }
458
+
459
+ // 克隆已选树数据
460
+ const _ticked = [...treeTicked.value]
461
+
462
+ // 获取值在树数据中的索引
463
+ const index = $n_indexOf(_ticked, id)
464
+
465
+ // 如果在数据中
466
+ if (index > -1) {
467
+ // 则删除
468
+ _ticked.splice(index, 1)
469
+ // 否则
470
+ } else {
471
+ // 添加
472
+ _ticked.push(id)
473
+ }
474
+
475
+ // 触发更新值
476
+ // 设置树选择数据
477
+ emitModelValue(_ticked)
478
+
479
+ // 否则是单选
480
+ } else {
481
+
482
+ if (
483
+ // 如果是父节点
484
+ $n_isValidArray(children)
485
+ // 如果仅可选择叶子节点
486
+ && ! props.strict
487
+ ) {
488
+ // 则无任何操作
489
+ return
490
+ }
491
+
492
+ // 触发更新值
493
+ // 设置树选择数据
494
+ emitModelValue(id)
495
+
496
+ // 则停止冒泡
497
+ e.preventDefault()
498
+ e.stopPropagation()
499
+
500
+ // 则关闭弹出层
501
+ popupRef.value.hide()
502
+ }
503
+ }
504
+
505
+ /**
506
+ * 移除单个节点
507
+ */
508
+ function onRemoveItem(index) {
509
+
510
+ // 克隆已选树数据
511
+ const _ticked = [...treeTicked.value]
512
+
513
+ // 删除该节点
514
+ _ticked.splice(index, 1)
515
+
516
+ // 触发更新值
517
+ // 设置树选择数据
518
+ emitModelValue(_ticked)
519
+ }
520
+
521
+ /**
522
+ * 字段获取焦点触发
523
+ */
524
+ function onFieldFocus(e) {
525
+
526
+ // 停止冒泡
527
+ e.stopPropagation()
528
+
529
+ // 设置输入框焦点
530
+ setInputFocus()
531
+
532
+ // window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0)
533
+ }
534
+
535
+ /**
536
+ * 字段失去焦点触发
537
+ */
538
+ function onFieldBlur(e) {
539
+
540
+ // 停止冒泡
541
+ e.stopPropagation()
542
+
543
+ if (
544
+ // 如果开启筛选
545
+ props.filter
546
+ // 如果没有显示弹出层
547
+ && ! showPopup.value
548
+ ) {
549
+ // 清空输入框值
550
+ inputValue.value = ''
551
+ }
552
+ }
553
+
554
+ /**
555
+ * 字段清空触发
556
+ */
557
+ function onFieldClear() {
558
+
559
+ // 触发更新值
560
+ // 清空树数据
561
+ emitModelValue(props.multiple ? [] : null)
562
+
563
+ // 隐藏弹出层
564
+ popupRef.value.hide()
565
+ }
566
+
567
+ /**
568
+ * 弹出层显示回调
569
+ */
570
+ async function onPopupShow() {
571
+
572
+ // 显示弹出层
573
+ showPopup.value = true
574
+
575
+ // 设置输入框焦点
576
+ setInputFocus()
577
+
578
+ if (
579
+ // 如果树已加载过了
580
+ __treeNodesLoaded
581
+ // 如果树已显示
582
+ || showTree.value
583
+ ) {
584
+ // 则无任何操作
585
+ return
586
+ }
587
+
588
+ // 初始加载节点
589
+ await defaultLoadNodes()
590
+ }
591
+
592
+ /**
593
+ * 设置输入框文字选中
594
+ */
595
+ function setInputSelection() {
596
+ if (
597
+ // 如果开启筛选
598
+ props.filter
599
+ // 如果有输入框节点
600
+ && inputRef.value
601
+ // 如果输入框有值
602
+ && inputValue.value.length
603
+ ) {
604
+ // 全选文字
605
+ inputRef.value.select()
606
+ // inputRef.value.setSelectionRange(0, inputValue.value.length)
607
+ }
608
+ }
609
+
610
+ /**
611
+ * 设置输入框焦点
612
+ */
613
+ function setInputFocus() {
614
+ if (
615
+ // 如果开启筛选
616
+ props.filter
617
+ // 如果有输入框节点
618
+ && inputRef.value
619
+ ) {
620
+ inputRef.value.focus()
621
+ }
622
+ }
623
+
624
+ /**
625
+ * 树节点是否激活
626
+ */
627
+ function checkTreeNodeActive({ id }) {
628
+ return $n_indexOf(treeTicked.value, id) > -1
629
+ }
630
+
631
+ /**
632
+ * 初始加载节点
633
+ */
634
+ async function defaultLoadNodes() {
635
+
636
+ // 如果是初始加载树节点树
637
+ if ($n_isFunction(props.nodes)) {
638
+
639
+ // 隐藏树
640
+ showTree.value = false
641
+
642
+ // 下次 DOM 更新
643
+ await nextTick()
644
+
645
+ // 通过自定义方法获取树节点数组
646
+ const resNodes = await $n_runAsync(props.nodes)()
647
+
648
+ if ($n_isValidArray(resNodes)) {
649
+
650
+ // 设置当前树节点数组
651
+ currentTreeNodes.value = resNodes
652
+
653
+ // 设置 tree all
654
+ treeAll.value = getTreeAll()
655
+
656
+ // 设置开数据
657
+ treeExpanded.value = getExpanded()
658
+
659
+ } else {
660
+
661
+ // 设置当前树节点数组
662
+ currentTreeNodes.value = []
663
+
664
+ // 设置 tree all
665
+ treeAll.value = getTreeAll()
666
+ }
667
+
668
+ // 否则为节点数组数据
669
+ } else {
670
+
671
+ // 设置当前树节点数组
672
+ currentTreeNodes.value = $n_isValidArray(props.nodes) ? props.nodes : []
673
+
674
+ // 设置 tree all
675
+ treeAll.value = getTreeAll()
676
+ }
677
+
678
+ // 设置显示树
679
+ showTree.value = true
680
+
681
+ // 树已加载
682
+ __treeNodesLoaded = true
683
+ }
684
+
685
+ // ==========【生命周期】=========================================================================================
686
+
687
+ /**
688
+ * 在组件因为响应式状态变更而更新其 DOM 树之后调用
689
+ */
690
+ onUpdated(function () {
691
+ if ($n_has(popupRef.value, 'currentComponent.ref.updatePosition')) {
692
+ popupRef.value.currentComponent.ref.updatePosition()
693
+ }
694
+ })
695
+
696
+ // ==========【返回】=============================================================================================
697
+
698
+ return {
699
+ // 显示值
700
+ showValue,
701
+ // 输入框节点
702
+ inputRef,
703
+ // 输入框值
704
+ inputValue,
705
+ // 弹出层节点
706
+ popupRef,
707
+ // 是否显示弹出层
708
+ showPopup,
709
+ // 是否显示树
710
+ showTree,
711
+ // 树节点
712
+ treeRef,
713
+ // 树选择数据
714
+ treeTicked,
715
+ // 树选择节点数据
716
+ treeTickedNodes,
717
+ // 树展开数据
718
+ treeExpanded,
719
+ // 当前树节点数据
720
+ currentTreeNodes,
721
+
722
+ // 触发更新值
723
+ emitModelValue,
724
+ // 点击节点
725
+ onNode,
726
+ // 移除单个
727
+ onRemoveItem,
728
+
729
+ // 字段获取焦点触发
730
+ onFieldFocus,
731
+ // 字段失去焦点触发
732
+ onFieldBlur,
733
+ // 字段清空触发
734
+ onFieldClear,
735
+
736
+ // 弹出层显示回调
737
+ onPopupShow,
738
+
739
+ // 节点是否激活
740
+ checkTreeNodeActive,
741
+ }
742
+ },
743
+ }
744
+ </script>
745
+
746
+ <style lang="scss">
747
+ .n-field-tree {
748
+ .q-field__input--padding {
749
+ padding-left: 4px;
750
+ min-width: 50px !important;
751
+ cursor: text;
752
+ }
753
+ }
754
+ </style>