evui 3.3.36 → 3.3.39
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/LICENSE +21 -21
- package/README.md +40 -40
- package/dist/evui.common.js +1907 -1832
- package/dist/evui.common.js.map +1 -1
- package/dist/evui.umd.js +1907 -1832
- package/dist/evui.umd.js.map +1 -1
- package/dist/evui.umd.min.js +1 -1
- package/dist/evui.umd.min.js.map +1 -1
- package/dist/img/{EVUI.7f3588fb.svg → EVUI.b82ee81a.svg} +292 -292
- package/dist/img/{icon_mysql.7ea26d5d.svg → icon_mysql.1085fdc9.svg} +78 -78
- package/dist/img/{icon_oracle.9009b108.svg → icon_oracle.0572d3ee.svg} +13 -13
- package/dist/img/{icon_postgresql.f8fffba9.svg → icon_postgresql.ee12bde8.svg} +58 -58
- package/package.json +61 -61
- package/src/common/emitter.js +20 -20
- package/src/common/utils.debounce.js +223 -223
- package/src/common/utils.js +134 -134
- package/src/common/utils.table.js +78 -78
- package/src/common/utils.throttle.js +83 -83
- package/src/common/utils.tree.js +18 -18
- package/src/components/button/Button.vue +198 -198
- package/src/components/button/index.js +7 -7
- package/src/components/buttonGroup/ButtonGroup.vue +11 -11
- package/src/components/buttonGroup/index.js +7 -7
- package/src/components/calendar/Calendar.vue +661 -661
- package/src/components/calendar/index.js +7 -7
- package/src/components/calendar/uses.js +1272 -1272
- package/src/components/chart/Chart.vue +189 -192
- package/src/components/chart/chart.core.js +870 -870
- package/src/components/chart/element/element.bar.js +524 -524
- package/src/components/chart/element/element.bar.time.js +156 -156
- package/src/components/chart/element/element.heatmap.js +533 -533
- package/src/components/chart/element/element.line.js +339 -339
- package/src/components/chart/element/element.pie.js +197 -197
- package/src/components/chart/element/element.scatter.js +184 -184
- package/src/components/chart/element/element.tip.js +550 -542
- package/src/components/chart/helpers/helpers.canvas.js +265 -265
- package/src/components/chart/helpers/helpers.constant.js +206 -206
- package/src/components/chart/helpers/helpers.util.js +346 -338
- package/src/components/chart/index.js +9 -9
- package/src/components/chart/model/index.js +4 -4
- package/src/components/chart/model/model.series.js +93 -93
- package/src/components/chart/model/model.store.js +977 -967
- package/src/components/chart/plugins/plugins.interaction.js +769 -769
- package/src/components/chart/plugins/plugins.legend.gradient.js +602 -602
- package/src/components/chart/plugins/plugins.legend.js +1155 -1151
- package/src/components/chart/plugins/plugins.pie.js +254 -254
- package/src/components/chart/plugins/plugins.title.js +56 -56
- package/src/components/chart/plugins/plugins.tooltip.js +692 -692
- package/src/components/chart/scale/scale.js +848 -848
- package/src/components/chart/scale/scale.linear.js +38 -38
- package/src/components/chart/scale/scale.logarithmic.js +128 -128
- package/src/components/chart/scale/scale.step.js +336 -336
- package/src/components/chart/scale/scale.time.category.js +277 -277
- package/src/components/chart/scale/scale.time.js +48 -48
- package/src/components/chart/style/chart.scss +312 -312
- package/src/components/chart/uses.js +264 -252
- package/src/components/checkbox/Checkbox.vue +200 -200
- package/src/components/checkbox/index.js +7 -7
- package/src/components/checkboxGroup/CheckboxGroup.vue +44 -44
- package/src/components/checkboxGroup/index.js +7 -7
- package/src/components/contextMenu/ContextMenu.vue +80 -80
- package/src/components/contextMenu/MenuList.vue +149 -149
- package/src/components/contextMenu/index.js +7 -7
- package/src/components/contextMenu/uses.js +203 -203
- package/src/components/datePicker/DatePicker.vue +437 -437
- package/src/components/datePicker/index.js +7 -7
- package/src/components/datePicker/uses.js +419 -419
- package/src/components/grid/Grid.vue +827 -827
- package/src/components/grid/grid.filter.window.vue +493 -493
- package/src/components/grid/grid.pagination.vue +75 -75
- package/src/components/grid/grid.summary.vue +265 -265
- package/src/components/grid/grid.toolbar.vue +26 -26
- package/src/components/grid/index.js +11 -11
- package/src/components/grid/style/grid.scss +263 -263
- package/src/components/grid/uses.js +1002 -1007
- package/src/components/icon/Icon.vue +49 -49
- package/src/components/icon/index.js +8 -8
- package/src/components/inputNumber/InputNumber.vue +212 -212
- package/src/components/inputNumber/index.js +7 -7
- package/src/components/inputNumber/uses.js +217 -217
- package/src/components/loading/Loading.vue +125 -125
- package/src/components/loading/index.js +7 -7
- package/src/components/menu/Menu.vue +68 -68
- package/src/components/menu/MenuItem.vue +187 -187
- package/src/components/menu/index.js +7 -7
- package/src/components/message/Message.vue +223 -223
- package/src/components/message/index.js +31 -31
- package/src/components/messageBox/MessageBox.vue +358 -358
- package/src/components/messageBox/index.js +22 -22
- package/src/components/notification/Notification.vue +316 -316
- package/src/components/notification/index.js +49 -49
- package/src/components/pagination/Pagination.vue +271 -271
- package/src/components/pagination/index.js +7 -7
- package/src/components/pagination/pageButton.vue +30 -30
- package/src/components/progress/Progress.vue +139 -139
- package/src/components/progress/index.js +7 -7
- package/src/components/radio/Radio.vue +159 -159
- package/src/components/radio/index.js +7 -7
- package/src/components/radioGroup/RadioGroup.vue +41 -41
- package/src/components/radioGroup/index.js +7 -7
- package/src/components/scheduler/Scheduler.vue +149 -149
- package/src/components/scheduler/index.js +7 -7
- package/src/components/scheduler/uses.js +183 -183
- package/src/components/select/Select.vue +440 -440
- package/src/components/select/index.js +7 -7
- package/src/components/select/uses.js +270 -270
- package/src/components/slider/Slider.vue +505 -505
- package/src/components/slider/index.js +7 -7
- package/src/components/slider/uses.js +390 -390
- package/src/components/tabPanel/TabPanel.vue +74 -74
- package/src/components/tabPanel/index.js +7 -7
- package/src/components/tabs/Tabs.vue +517 -517
- package/src/components/tabs/index.js +7 -7
- package/src/components/textField/TextField.vue +375 -375
- package/src/components/textField/index.js +7 -7
- package/src/components/timePicker/TimePicker.vue +352 -352
- package/src/components/timePicker/index.js +7 -7
- package/src/components/toggle/Toggle.vue +115 -115
- package/src/components/toggle/index.js +7 -7
- package/src/components/tree/Tree.vue +313 -313
- package/src/components/tree/TreeNode.vue +293 -293
- package/src/components/tree/index.js +7 -7
- package/src/components/treeGrid/TreeGrid.vue +758 -758
- package/src/components/treeGrid/TreeGridNode.vue +275 -275
- package/src/components/treeGrid/index.js +9 -9
- package/src/components/treeGrid/style/treeGrid.scss +261 -261
- package/src/components/treeGrid/treeGrid.toolbar.vue +26 -26
- package/src/components/treeGrid/uses.js +867 -867
- package/src/components/window/Window.vue +329 -329
- package/src/components/window/index.js +7 -7
- package/src/components/window/uses.js +899 -899
- package/src/directives/clickoutside.js +90 -90
- package/src/main.js +116 -116
- package/src/style/components/input.scss +108 -108
- package/src/style/functions.scss +3 -3
- package/src/style/index.scss +6 -6
- package/src/style/lib/fonts/EVUI.svg +292 -292
- package/src/style/lib/icon.css +888 -888
- package/src/style/mixins.scss +94 -94
- package/src/style/themes.scss +67 -67
- package/src/style/variables.scss +22 -22
|
@@ -1,313 +1,313 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="ev-tree-view">
|
|
3
|
-
<tree-node
|
|
4
|
-
v-for="(item, i) in treeNodeData"
|
|
5
|
-
:key="i"
|
|
6
|
-
:data="item"
|
|
7
|
-
:use-checkbox="useCheckbox"
|
|
8
|
-
:expand-icon="expandIcon"
|
|
9
|
-
:collapse-icon="collapseIcon"
|
|
10
|
-
:comp="component"
|
|
11
|
-
@update-checked-info="updateCheckedInfo"
|
|
12
|
-
@click-node="clickContent"
|
|
13
|
-
@dblclick-node="dblClickContent"
|
|
14
|
-
@show-context-menu="getContextMenuFlag"
|
|
15
|
-
@contextmenu.prevent="showContextMenu"
|
|
16
|
-
/>
|
|
17
|
-
<div v-if="!treeNodeData.length">No data</div>
|
|
18
|
-
<ev-context-menu
|
|
19
|
-
v-if="contextMenuItems.length"
|
|
20
|
-
ref="contextMenu"
|
|
21
|
-
:items="contextMenuItems"
|
|
22
|
-
/>
|
|
23
|
-
</div>
|
|
24
|
-
</template>
|
|
25
|
-
|
|
26
|
-
<script>
|
|
27
|
-
import { ref, reactive, watch, onMounted, onBeforeUnmount } from 'vue';
|
|
28
|
-
import TreeNode from './TreeNode';
|
|
29
|
-
|
|
30
|
-
export default {
|
|
31
|
-
name: 'EvTree',
|
|
32
|
-
components: { TreeNode },
|
|
33
|
-
props: {
|
|
34
|
-
data: {
|
|
35
|
-
type: Array,
|
|
36
|
-
default: () => [],
|
|
37
|
-
},
|
|
38
|
-
useCheckbox: {
|
|
39
|
-
type: Boolean,
|
|
40
|
-
default: false,
|
|
41
|
-
},
|
|
42
|
-
emptyText: {
|
|
43
|
-
type: String,
|
|
44
|
-
default: 'No Data',
|
|
45
|
-
},
|
|
46
|
-
expandIcon: {
|
|
47
|
-
type: String,
|
|
48
|
-
default: '',
|
|
49
|
-
},
|
|
50
|
-
collapseIcon: {
|
|
51
|
-
type: String,
|
|
52
|
-
default: '',
|
|
53
|
-
},
|
|
54
|
-
contextMenuItems: {
|
|
55
|
-
type: Array,
|
|
56
|
-
default: () => [],
|
|
57
|
-
},
|
|
58
|
-
searchWord: {
|
|
59
|
-
type: String,
|
|
60
|
-
default: '',
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
emits: {
|
|
64
|
-
'click-node': null,
|
|
65
|
-
'dblclick-node': null,
|
|
66
|
-
check: Array,
|
|
67
|
-
},
|
|
68
|
-
setup(props, { emit }) {
|
|
69
|
-
let treeNodeData = reactive(props.data);
|
|
70
|
-
let allNodeInfo = [];
|
|
71
|
-
const contextMenu = ref(null);
|
|
72
|
-
let contextMenuFlag = false; // flag for showing contextMenu or not
|
|
73
|
-
const component = TreeNode;
|
|
74
|
-
|
|
75
|
-
function updateTreeUp(nodeKey) {
|
|
76
|
-
const parentKey = allNodeInfo[nodeKey].parent;
|
|
77
|
-
if (typeof parentKey === 'undefined') {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
const node = allNodeInfo[nodeKey].node;
|
|
81
|
-
const parent = allNodeInfo[parentKey].node;
|
|
82
|
-
if (node.checked === parent.checked && node.indeterminate === parent.indeterminate) {
|
|
83
|
-
return; // no need to update upwards
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (node.checked) {
|
|
87
|
-
parent.checked = parent.children.every(n => n.checked);
|
|
88
|
-
parent.indeterminate = !parent.checked;
|
|
89
|
-
} else {
|
|
90
|
-
const fn = n => n.checked || node.indeterminate;
|
|
91
|
-
parent.checked = false;
|
|
92
|
-
parent.indeterminate = parent.children.some(fn);
|
|
93
|
-
}
|
|
94
|
-
updateTreeUp(parentKey);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function updateTreeDown(node, changes = {}) {
|
|
98
|
-
const keys = Object.keys(changes);
|
|
99
|
-
for (let ix = 0; ix < keys.length; ix++) {
|
|
100
|
-
node[keys[ix]] = changes[keys[ix]]; // eslint-disable-line no-param-reassign
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (node.children) {
|
|
104
|
-
node.children.forEach((child) => {
|
|
105
|
-
updateTreeDown(child, changes);
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function getAllNodeInfo() { // return the array to easily search parents and children
|
|
111
|
-
let keyCounter = 0;
|
|
112
|
-
const flatTree = [];
|
|
113
|
-
const valueArr = [];
|
|
114
|
-
|
|
115
|
-
function flattenChildren(n, parent) {
|
|
116
|
-
const node = n;
|
|
117
|
-
node.nodeKey = keyCounter++;
|
|
118
|
-
|
|
119
|
-
// add 'selected' property for highlighting clicked node
|
|
120
|
-
if (!Object.hasOwnProperty.call(node, 'selected')) {
|
|
121
|
-
node.selected = false;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// add 'visible' property for filtering node
|
|
125
|
-
if (!Object.hasOwnProperty.call(node, 'visible')) {
|
|
126
|
-
node.visible = true;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// check 'value' property and add nodeKey if same value already exists
|
|
130
|
-
if ('value' in node && valueArr.includes(node.value)) {
|
|
131
|
-
console.warn('The \'value\' of data should be unique.');
|
|
132
|
-
node.value += node.nodeKey;
|
|
133
|
-
} else if (!('value' in node)) {
|
|
134
|
-
node.value = node.title + node.nodeKey;
|
|
135
|
-
}
|
|
136
|
-
valueArr.push(node.value);
|
|
137
|
-
|
|
138
|
-
flatTree[node.nodeKey] = { node, nodeKey: node.nodeKey };
|
|
139
|
-
if (typeof parent !== 'undefined') {
|
|
140
|
-
flatTree[node.nodeKey].parent = parent.nodeKey;
|
|
141
|
-
flatTree[parent.nodeKey].children.push(node.nodeKey);
|
|
142
|
-
}
|
|
143
|
-
if (node.children) {
|
|
144
|
-
flatTree[node.nodeKey].children = [];
|
|
145
|
-
node.children.forEach(child => flattenChildren(child, node));
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
flattenChildren(treeNodeData[0]);
|
|
150
|
-
return flatTree;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
allNodeInfo = getAllNodeInfo();
|
|
154
|
-
|
|
155
|
-
function getCheckedNodes() {
|
|
156
|
-
return allNodeInfo.filter(obj => obj.node.checked).map(obj => obj.node);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function rebuildTree() { // rebuild the tree through checked nodes
|
|
160
|
-
const checkedNodes = getCheckedNodes();
|
|
161
|
-
checkedNodes.forEach((node) => {
|
|
162
|
-
updateTreeDown(node, { checked: true });
|
|
163
|
-
// propagate upwards
|
|
164
|
-
const parentKey = allNodeInfo[node.nodeKey].parent;
|
|
165
|
-
if (!parentKey && parentKey !== 0) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
const parent = allNodeInfo[parentKey].node;
|
|
169
|
-
const childHasCheckSetter = typeof node.checked !== 'undefined' && node.checked;
|
|
170
|
-
if (childHasCheckSetter && parent.checked !== node.checked) {
|
|
171
|
-
updateTreeUp(node.nodeKey); // update tree upwards
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function updateCheckedInfo({ nodeKey, isChecked }) {
|
|
177
|
-
const node = allNodeInfo[nodeKey].node;
|
|
178
|
-
node.checked = isChecked;
|
|
179
|
-
node.indeterminate = false;
|
|
180
|
-
updateTreeUp(nodeKey); // propagate up
|
|
181
|
-
updateTreeDown(node, { checked: isChecked, indeterminate: false }); // reset `indeterminate`
|
|
182
|
-
const checkedNodes = allNodeInfo.filter(obj => obj.node.checked)
|
|
183
|
-
.map(obj => ({
|
|
184
|
-
title: obj.node.title,
|
|
185
|
-
value: obj.node.value,
|
|
186
|
-
}));
|
|
187
|
-
emit('check', checkedNodes);
|
|
188
|
-
rebuildTree();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function clickContent(nodeKey) {
|
|
192
|
-
const clickedNode = allNodeInfo[nodeKey].node;
|
|
193
|
-
// reset other selected node to false
|
|
194
|
-
for (let ix = 0; ix < allNodeInfo.length; ix++) {
|
|
195
|
-
if (allNodeInfo[ix].node.nodeKey !== nodeKey) {
|
|
196
|
-
allNodeInfo[ix].node.selected = false;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
emit('click-node', { title: clickedNode.title, value: clickedNode.value });
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function dblClickContent(nodeKey) {
|
|
203
|
-
const dbClickedContent = allNodeInfo[nodeKey].node;
|
|
204
|
-
emit('dblclick-node', { title: dbClickedContent.title, value: dbClickedContent.value });
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const showContextMenu = (e) => {
|
|
208
|
-
if (props.contextMenuItems.length && contextMenuFlag) {
|
|
209
|
-
contextMenu.value.show(e);
|
|
210
|
-
contextMenuFlag = false; // reset contextmenuFlag
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
const getContextMenuFlag = (isShow, e) => {
|
|
215
|
-
contextMenuFlag = isShow;
|
|
216
|
-
showContextMenu(e);
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
function makeChildrenInvisible(node) {
|
|
220
|
-
if (node.children) {
|
|
221
|
-
node.children.forEach((child) => {
|
|
222
|
-
child.visible = false; // eslint-disable-line no-param-reassign
|
|
223
|
-
makeChildrenInvisible(child);
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function makeParentVisible(parentKey) {
|
|
229
|
-
if (!parentKey && parentKey !== 0) {
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const parent = allNodeInfo[parentKey];
|
|
234
|
-
parent.node.visible = true;
|
|
235
|
-
if (parent.parent !== undefined) {
|
|
236
|
-
makeParentVisible(parent.parent);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function filterNode(value) {
|
|
241
|
-
allNodeInfo.forEach((nodeObj) => {
|
|
242
|
-
const node = nodeObj.node;
|
|
243
|
-
node.visible = false;
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
const filteredNodes = allNodeInfo.filter(nodeObj => nodeObj.node.title.includes(value));
|
|
247
|
-
|
|
248
|
-
filteredNodes.forEach((nodeObj) => {
|
|
249
|
-
const node = nodeObj.node;
|
|
250
|
-
node.visible = true;
|
|
251
|
-
// make children invisible, traverse down
|
|
252
|
-
makeChildrenInvisible(node);
|
|
253
|
-
// make parent visible, traverse up
|
|
254
|
-
const parentKey = allNodeInfo[node.nodeKey].parent;
|
|
255
|
-
makeParentVisible(parentKey);
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
watch(props.data, (newData) => {
|
|
260
|
-
treeNodeData = newData;
|
|
261
|
-
allNodeInfo = getAllNodeInfo();
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
let timer;
|
|
266
|
-
watch(() => props.searchWord, (newSearchWord) => {
|
|
267
|
-
if (timer) {
|
|
268
|
-
clearTimeout(timer);
|
|
269
|
-
}
|
|
270
|
-
timer = setTimeout(() => {
|
|
271
|
-
if (newSearchWord) {
|
|
272
|
-
filterNode(newSearchWord);
|
|
273
|
-
} else {
|
|
274
|
-
allNodeInfo.forEach((nodeObj) => {
|
|
275
|
-
const node = nodeObj.node;
|
|
276
|
-
node.visible = true;
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
}, 200);
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
onMounted(() => {
|
|
283
|
-
rebuildTree();
|
|
284
|
-
const checkedNodes = getCheckedNodes();
|
|
285
|
-
if (checkedNodes.length) {
|
|
286
|
-
emit('check', checkedNodes.map(node => ({
|
|
287
|
-
title: node.title,
|
|
288
|
-
value: node.value,
|
|
289
|
-
})));
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
onBeforeUnmount(() => {
|
|
293
|
-
if (timer) {
|
|
294
|
-
clearTimeout(timer);
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
return {
|
|
299
|
-
treeNodeData,
|
|
300
|
-
contextMenu,
|
|
301
|
-
component,
|
|
302
|
-
updateCheckedInfo,
|
|
303
|
-
clickContent,
|
|
304
|
-
dblClickContent,
|
|
305
|
-
showContextMenu,
|
|
306
|
-
getContextMenuFlag,
|
|
307
|
-
};
|
|
308
|
-
},
|
|
309
|
-
};
|
|
310
|
-
</script>
|
|
311
|
-
|
|
312
|
-
<style lang="scss">
|
|
313
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<div class="ev-tree-view">
|
|
3
|
+
<tree-node
|
|
4
|
+
v-for="(item, i) in treeNodeData"
|
|
5
|
+
:key="i"
|
|
6
|
+
:data="item"
|
|
7
|
+
:use-checkbox="useCheckbox"
|
|
8
|
+
:expand-icon="expandIcon"
|
|
9
|
+
:collapse-icon="collapseIcon"
|
|
10
|
+
:comp="component"
|
|
11
|
+
@update-checked-info="updateCheckedInfo"
|
|
12
|
+
@click-node="clickContent"
|
|
13
|
+
@dblclick-node="dblClickContent"
|
|
14
|
+
@show-context-menu="getContextMenuFlag"
|
|
15
|
+
@contextmenu.prevent="showContextMenu"
|
|
16
|
+
/>
|
|
17
|
+
<div v-if="!treeNodeData.length">No data</div>
|
|
18
|
+
<ev-context-menu
|
|
19
|
+
v-if="contextMenuItems.length"
|
|
20
|
+
ref="contextMenu"
|
|
21
|
+
:items="contextMenuItems"
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script>
|
|
27
|
+
import { ref, reactive, watch, onMounted, onBeforeUnmount } from 'vue';
|
|
28
|
+
import TreeNode from './TreeNode';
|
|
29
|
+
|
|
30
|
+
export default {
|
|
31
|
+
name: 'EvTree',
|
|
32
|
+
components: { TreeNode },
|
|
33
|
+
props: {
|
|
34
|
+
data: {
|
|
35
|
+
type: Array,
|
|
36
|
+
default: () => [],
|
|
37
|
+
},
|
|
38
|
+
useCheckbox: {
|
|
39
|
+
type: Boolean,
|
|
40
|
+
default: false,
|
|
41
|
+
},
|
|
42
|
+
emptyText: {
|
|
43
|
+
type: String,
|
|
44
|
+
default: 'No Data',
|
|
45
|
+
},
|
|
46
|
+
expandIcon: {
|
|
47
|
+
type: String,
|
|
48
|
+
default: '',
|
|
49
|
+
},
|
|
50
|
+
collapseIcon: {
|
|
51
|
+
type: String,
|
|
52
|
+
default: '',
|
|
53
|
+
},
|
|
54
|
+
contextMenuItems: {
|
|
55
|
+
type: Array,
|
|
56
|
+
default: () => [],
|
|
57
|
+
},
|
|
58
|
+
searchWord: {
|
|
59
|
+
type: String,
|
|
60
|
+
default: '',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
emits: {
|
|
64
|
+
'click-node': null,
|
|
65
|
+
'dblclick-node': null,
|
|
66
|
+
check: Array,
|
|
67
|
+
},
|
|
68
|
+
setup(props, { emit }) {
|
|
69
|
+
let treeNodeData = reactive(props.data);
|
|
70
|
+
let allNodeInfo = [];
|
|
71
|
+
const contextMenu = ref(null);
|
|
72
|
+
let contextMenuFlag = false; // flag for showing contextMenu or not
|
|
73
|
+
const component = TreeNode;
|
|
74
|
+
|
|
75
|
+
function updateTreeUp(nodeKey) {
|
|
76
|
+
const parentKey = allNodeInfo[nodeKey].parent;
|
|
77
|
+
if (typeof parentKey === 'undefined') {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const node = allNodeInfo[nodeKey].node;
|
|
81
|
+
const parent = allNodeInfo[parentKey].node;
|
|
82
|
+
if (node.checked === parent.checked && node.indeterminate === parent.indeterminate) {
|
|
83
|
+
return; // no need to update upwards
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (node.checked) {
|
|
87
|
+
parent.checked = parent.children.every(n => n.checked);
|
|
88
|
+
parent.indeterminate = !parent.checked;
|
|
89
|
+
} else {
|
|
90
|
+
const fn = n => n.checked || node.indeterminate;
|
|
91
|
+
parent.checked = false;
|
|
92
|
+
parent.indeterminate = parent.children.some(fn);
|
|
93
|
+
}
|
|
94
|
+
updateTreeUp(parentKey);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function updateTreeDown(node, changes = {}) {
|
|
98
|
+
const keys = Object.keys(changes);
|
|
99
|
+
for (let ix = 0; ix < keys.length; ix++) {
|
|
100
|
+
node[keys[ix]] = changes[keys[ix]]; // eslint-disable-line no-param-reassign
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (node.children) {
|
|
104
|
+
node.children.forEach((child) => {
|
|
105
|
+
updateTreeDown(child, changes);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getAllNodeInfo() { // return the array to easily search parents and children
|
|
111
|
+
let keyCounter = 0;
|
|
112
|
+
const flatTree = [];
|
|
113
|
+
const valueArr = [];
|
|
114
|
+
|
|
115
|
+
function flattenChildren(n, parent) {
|
|
116
|
+
const node = n;
|
|
117
|
+
node.nodeKey = keyCounter++;
|
|
118
|
+
|
|
119
|
+
// add 'selected' property for highlighting clicked node
|
|
120
|
+
if (!Object.hasOwnProperty.call(node, 'selected')) {
|
|
121
|
+
node.selected = false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// add 'visible' property for filtering node
|
|
125
|
+
if (!Object.hasOwnProperty.call(node, 'visible')) {
|
|
126
|
+
node.visible = true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// check 'value' property and add nodeKey if same value already exists
|
|
130
|
+
if ('value' in node && valueArr.includes(node.value)) {
|
|
131
|
+
console.warn('The \'value\' of data should be unique.');
|
|
132
|
+
node.value += node.nodeKey;
|
|
133
|
+
} else if (!('value' in node)) {
|
|
134
|
+
node.value = node.title + node.nodeKey;
|
|
135
|
+
}
|
|
136
|
+
valueArr.push(node.value);
|
|
137
|
+
|
|
138
|
+
flatTree[node.nodeKey] = { node, nodeKey: node.nodeKey };
|
|
139
|
+
if (typeof parent !== 'undefined') {
|
|
140
|
+
flatTree[node.nodeKey].parent = parent.nodeKey;
|
|
141
|
+
flatTree[parent.nodeKey].children.push(node.nodeKey);
|
|
142
|
+
}
|
|
143
|
+
if (node.children) {
|
|
144
|
+
flatTree[node.nodeKey].children = [];
|
|
145
|
+
node.children.forEach(child => flattenChildren(child, node));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
flattenChildren(treeNodeData[0]);
|
|
150
|
+
return flatTree;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
allNodeInfo = getAllNodeInfo();
|
|
154
|
+
|
|
155
|
+
function getCheckedNodes() {
|
|
156
|
+
return allNodeInfo.filter(obj => obj.node.checked).map(obj => obj.node);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function rebuildTree() { // rebuild the tree through checked nodes
|
|
160
|
+
const checkedNodes = getCheckedNodes();
|
|
161
|
+
checkedNodes.forEach((node) => {
|
|
162
|
+
updateTreeDown(node, { checked: true });
|
|
163
|
+
// propagate upwards
|
|
164
|
+
const parentKey = allNodeInfo[node.nodeKey].parent;
|
|
165
|
+
if (!parentKey && parentKey !== 0) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const parent = allNodeInfo[parentKey].node;
|
|
169
|
+
const childHasCheckSetter = typeof node.checked !== 'undefined' && node.checked;
|
|
170
|
+
if (childHasCheckSetter && parent.checked !== node.checked) {
|
|
171
|
+
updateTreeUp(node.nodeKey); // update tree upwards
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function updateCheckedInfo({ nodeKey, isChecked }) {
|
|
177
|
+
const node = allNodeInfo[nodeKey].node;
|
|
178
|
+
node.checked = isChecked;
|
|
179
|
+
node.indeterminate = false;
|
|
180
|
+
updateTreeUp(nodeKey); // propagate up
|
|
181
|
+
updateTreeDown(node, { checked: isChecked, indeterminate: false }); // reset `indeterminate`
|
|
182
|
+
const checkedNodes = allNodeInfo.filter(obj => obj.node.checked)
|
|
183
|
+
.map(obj => ({
|
|
184
|
+
title: obj.node.title,
|
|
185
|
+
value: obj.node.value,
|
|
186
|
+
}));
|
|
187
|
+
emit('check', checkedNodes);
|
|
188
|
+
rebuildTree();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function clickContent(nodeKey) {
|
|
192
|
+
const clickedNode = allNodeInfo[nodeKey].node;
|
|
193
|
+
// reset other selected node to false
|
|
194
|
+
for (let ix = 0; ix < allNodeInfo.length; ix++) {
|
|
195
|
+
if (allNodeInfo[ix].node.nodeKey !== nodeKey) {
|
|
196
|
+
allNodeInfo[ix].node.selected = false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
emit('click-node', { title: clickedNode.title, value: clickedNode.value });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function dblClickContent(nodeKey) {
|
|
203
|
+
const dbClickedContent = allNodeInfo[nodeKey].node;
|
|
204
|
+
emit('dblclick-node', { title: dbClickedContent.title, value: dbClickedContent.value });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const showContextMenu = (e) => {
|
|
208
|
+
if (props.contextMenuItems.length && contextMenuFlag) {
|
|
209
|
+
contextMenu.value.show(e);
|
|
210
|
+
contextMenuFlag = false; // reset contextmenuFlag
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const getContextMenuFlag = (isShow, e) => {
|
|
215
|
+
contextMenuFlag = isShow;
|
|
216
|
+
showContextMenu(e);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
function makeChildrenInvisible(node) {
|
|
220
|
+
if (node.children) {
|
|
221
|
+
node.children.forEach((child) => {
|
|
222
|
+
child.visible = false; // eslint-disable-line no-param-reassign
|
|
223
|
+
makeChildrenInvisible(child);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function makeParentVisible(parentKey) {
|
|
229
|
+
if (!parentKey && parentKey !== 0) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const parent = allNodeInfo[parentKey];
|
|
234
|
+
parent.node.visible = true;
|
|
235
|
+
if (parent.parent !== undefined) {
|
|
236
|
+
makeParentVisible(parent.parent);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function filterNode(value) {
|
|
241
|
+
allNodeInfo.forEach((nodeObj) => {
|
|
242
|
+
const node = nodeObj.node;
|
|
243
|
+
node.visible = false;
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const filteredNodes = allNodeInfo.filter(nodeObj => nodeObj.node.title.includes(value));
|
|
247
|
+
|
|
248
|
+
filteredNodes.forEach((nodeObj) => {
|
|
249
|
+
const node = nodeObj.node;
|
|
250
|
+
node.visible = true;
|
|
251
|
+
// make children invisible, traverse down
|
|
252
|
+
makeChildrenInvisible(node);
|
|
253
|
+
// make parent visible, traverse up
|
|
254
|
+
const parentKey = allNodeInfo[node.nodeKey].parent;
|
|
255
|
+
makeParentVisible(parentKey);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
watch(props.data, (newData) => {
|
|
260
|
+
treeNodeData = newData;
|
|
261
|
+
allNodeInfo = getAllNodeInfo();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
let timer;
|
|
266
|
+
watch(() => props.searchWord, (newSearchWord) => {
|
|
267
|
+
if (timer) {
|
|
268
|
+
clearTimeout(timer);
|
|
269
|
+
}
|
|
270
|
+
timer = setTimeout(() => {
|
|
271
|
+
if (newSearchWord) {
|
|
272
|
+
filterNode(newSearchWord);
|
|
273
|
+
} else {
|
|
274
|
+
allNodeInfo.forEach((nodeObj) => {
|
|
275
|
+
const node = nodeObj.node;
|
|
276
|
+
node.visible = true;
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}, 200);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
onMounted(() => {
|
|
283
|
+
rebuildTree();
|
|
284
|
+
const checkedNodes = getCheckedNodes();
|
|
285
|
+
if (checkedNodes.length) {
|
|
286
|
+
emit('check', checkedNodes.map(node => ({
|
|
287
|
+
title: node.title,
|
|
288
|
+
value: node.value,
|
|
289
|
+
})));
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
onBeforeUnmount(() => {
|
|
293
|
+
if (timer) {
|
|
294
|
+
clearTimeout(timer);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
treeNodeData,
|
|
300
|
+
contextMenu,
|
|
301
|
+
component,
|
|
302
|
+
updateCheckedInfo,
|
|
303
|
+
clickContent,
|
|
304
|
+
dblClickContent,
|
|
305
|
+
showContextMenu,
|
|
306
|
+
getContextMenuFlag,
|
|
307
|
+
};
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
</script>
|
|
311
|
+
|
|
312
|
+
<style lang="scss">
|
|
313
|
+
</style>
|