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