evui 3.1.38 → 3.1.42
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/dist/evui.common.js +2699 -970
- package/dist/evui.common.js.map +1 -1
- package/dist/evui.umd.js +2699 -970
- 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/package.json +1 -1
- package/src/components/calendar/Calendar.vue +194 -82
- package/src/components/calendar/uses.js +457 -138
- package/src/components/chart/chart.core.js +27 -23
- package/src/components/chart/element/element.bar.js +28 -20
- package/src/components/chart/element/element.line.js +13 -0
- package/src/components/chart/helpers/helpers.constant.js +27 -0
- package/src/components/chart/helpers/helpers.util.js +2 -2
- package/src/components/chart/plugins/plugins.interaction.js +5 -5
- package/src/components/chart/plugins/plugins.legend.js +6 -3
- package/src/components/chart/plugins/plugins.tooltip.js +14 -1
- package/src/components/chart/scale/scale.js +520 -56
- package/src/components/chart/scale/scale.linear.js +8 -0
- package/src/components/chart/scale/scale.logarithmic.js +8 -0
- package/src/components/chart/scale/scale.step.js +148 -69
- package/src/components/chart/scale/scale.time.category.js +8 -0
- package/src/components/chart/scale/scale.time.js +8 -0
- package/src/components/chart/uses.js +2 -2
- package/src/components/contextMenu/MenuList.vue +1 -1
- package/src/components/datePicker/DatePicker.vue +91 -29
- package/src/components/datePicker/uses.js +231 -21
- package/src/components/grid/Grid.vue +15 -10
- package/src/components/tabs/Tabs.vue +12 -11
- package/src/components/treeGrid/TreeGrid.vue +56 -2
- package/src/components/treeGrid/TreeGridNode.vue +10 -1
- package/src/components/treeGrid/treeGrid.toolbar.vue +26 -0
- package/src/components/treeGrid/uses.js +66 -6
|
@@ -2,12 +2,14 @@ import {
|
|
|
2
2
|
ref, reactive, computed, watch,
|
|
3
3
|
nextTick, getCurrentInstance,
|
|
4
4
|
} from 'vue';
|
|
5
|
+
import { getChangedValueByTimeFormat } from '../calendar/uses';
|
|
5
6
|
|
|
6
7
|
const dateReg = new RegExp(/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/);
|
|
7
8
|
const dateTimeReg = new RegExp(/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9]/);
|
|
8
9
|
|
|
9
10
|
export const useModel = () => {
|
|
10
11
|
const { props, emit } = getCurrentInstance();
|
|
12
|
+
const timeFormat = props.options?.timeFormat;
|
|
11
13
|
|
|
12
14
|
// Select 컴포넌트의 v-model 값
|
|
13
15
|
const mv = computed({
|
|
@@ -15,13 +17,42 @@ export const useModel = () => {
|
|
|
15
17
|
if (!props.modelValue) {
|
|
16
18
|
return (props.mode === 'date' || props.mode === 'dateTime') ? '' : [];
|
|
17
19
|
}
|
|
20
|
+
if (['dateTime', 'dateTimeRange'].includes(props.mode) && timeFormat) {
|
|
21
|
+
if (props.mode === 'dateTime') {
|
|
22
|
+
return getChangedValueByTimeFormat(timeFormat, props.modelValue);
|
|
23
|
+
} else if (props.modelValue.length) {
|
|
24
|
+
const [fromTimeFormat, toTimeFormat] = timeFormat;
|
|
25
|
+
return [
|
|
26
|
+
getChangedValueByTimeFormat(fromTimeFormat, props.modelValue[0]),
|
|
27
|
+
getChangedValueByTimeFormat(toTimeFormat, props.modelValue[1]),
|
|
28
|
+
];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
18
31
|
return props.modelValue;
|
|
19
32
|
},
|
|
20
33
|
set: value => emit('update:modelValue', value),
|
|
21
34
|
});
|
|
22
35
|
|
|
23
36
|
// mode: 'date' or 'dateTime'시 input box의 입력된 텍스트값
|
|
24
|
-
|
|
37
|
+
let currentValue;
|
|
38
|
+
if (['dateTimeRange', 'dateTime'].includes(props.mode) && timeFormat) {
|
|
39
|
+
if (props.mode === 'dateTimeRange' && props.modelValue.length) {
|
|
40
|
+
const [fromDate, toDate] = props.modelValue;
|
|
41
|
+
const [fromTimeFormat, toTimeFormat] = timeFormat;
|
|
42
|
+
|
|
43
|
+
props.modelValue = [
|
|
44
|
+
getChangedValueByTimeFormat(fromTimeFormat, fromDate),
|
|
45
|
+
getChangedValueByTimeFormat(toTimeFormat, toDate),
|
|
46
|
+
];
|
|
47
|
+
currentValue = ref(props.modelValue);
|
|
48
|
+
} else if (props.mode === 'dateTime' && props.modelValue) {
|
|
49
|
+
currentValue = ref(getChangedValueByTimeFormat(timeFormat, props.modelValue));
|
|
50
|
+
} else {
|
|
51
|
+
currentValue = ref(props.modelValue);
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
currentValue = ref(props.modelValue);
|
|
55
|
+
}
|
|
25
56
|
|
|
26
57
|
const validateValue = (curr) => {
|
|
27
58
|
if (props.mode === 'date'
|
|
@@ -89,9 +120,8 @@ export const useModel = () => {
|
|
|
89
120
|
};
|
|
90
121
|
};
|
|
91
122
|
|
|
92
|
-
export const useDropdown = (
|
|
123
|
+
export const useDropdown = () => {
|
|
93
124
|
const { props } = getCurrentInstance();
|
|
94
|
-
const { currentValue } = param;
|
|
95
125
|
|
|
96
126
|
const isDropbox = ref(false);
|
|
97
127
|
const datePicker = ref(null);
|
|
@@ -162,24 +192,6 @@ export const useDropdown = (param) => {
|
|
|
162
192
|
isDropbox.value = false;
|
|
163
193
|
};
|
|
164
194
|
|
|
165
|
-
watch(
|
|
166
|
-
() => props.modelValue,
|
|
167
|
-
(curr) => {
|
|
168
|
-
if (props.mode === 'dateMulti'
|
|
169
|
-
&& props?.options?.multiType === 'date'
|
|
170
|
-
&& props?.options?.multiDayLimit > curr.length
|
|
171
|
-
) {
|
|
172
|
-
return;
|
|
173
|
-
} else if (props.mode === 'dateTime') {
|
|
174
|
-
currentValue.value = curr;
|
|
175
|
-
return;
|
|
176
|
-
} else if (props.mode === 'date') {
|
|
177
|
-
currentValue.value = curr;
|
|
178
|
-
}
|
|
179
|
-
clickOutsideDropbox();
|
|
180
|
-
},
|
|
181
|
-
);
|
|
182
|
-
|
|
183
195
|
return {
|
|
184
196
|
isDropbox,
|
|
185
197
|
datePicker,
|
|
@@ -191,3 +203,201 @@ export const useDropdown = (param) => {
|
|
|
191
203
|
changeDropboxPosition,
|
|
192
204
|
};
|
|
193
205
|
};
|
|
206
|
+
|
|
207
|
+
export const useShortcuts = (param) => {
|
|
208
|
+
const { props } = getCurrentInstance();
|
|
209
|
+
const { mv, currentValue, clickOutsideDropbox } = param;
|
|
210
|
+
|
|
211
|
+
const usedShortcuts = reactive([]);
|
|
212
|
+
props.shortcuts?.forEach(({ value, label, shortcutDate }) => {
|
|
213
|
+
usedShortcuts.push({
|
|
214
|
+
key: value,
|
|
215
|
+
label,
|
|
216
|
+
shortcutDate,
|
|
217
|
+
isActive: false,
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* active 되어있는 shortcut 제거
|
|
223
|
+
*/
|
|
224
|
+
const clearShortcuts = () => {
|
|
225
|
+
const targetShortcut = usedShortcuts.find(shortcut => shortcut.isActive);
|
|
226
|
+
if (targetShortcut) {
|
|
227
|
+
targetShortcut.isActive = false;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* targetKey에 해당하는 shortcut을 active
|
|
233
|
+
* @param targetKey
|
|
234
|
+
*/
|
|
235
|
+
const activeShortcut = (targetKey) => {
|
|
236
|
+
const targetShortcut = usedShortcuts.find(shortcut => shortcut.key === targetKey);
|
|
237
|
+
if (targetShortcut) {
|
|
238
|
+
targetShortcut.isActive = true;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 월, 일을 두자리 숫자로 보정
|
|
244
|
+
* @param num
|
|
245
|
+
* @returns {string|*}
|
|
246
|
+
*/
|
|
247
|
+
const lpadToTwoDigits = (num) => {
|
|
248
|
+
if (num === null) {
|
|
249
|
+
return '00';
|
|
250
|
+
} else if (+num < 10) {
|
|
251
|
+
return `0${num}`;
|
|
252
|
+
}
|
|
253
|
+
return num;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 'YYYY-MM-DD' 형식으로 format
|
|
258
|
+
* @param targetDate
|
|
259
|
+
* @returns string
|
|
260
|
+
*/
|
|
261
|
+
const formatDate = (targetDate) => {
|
|
262
|
+
const dateValue = targetDate ? new Date(targetDate) : new Date();
|
|
263
|
+
const year = dateValue.getFullYear();
|
|
264
|
+
const month = dateValue.getMonth() + 1;
|
|
265
|
+
const day = dateValue.getDate();
|
|
266
|
+
return `${year}-${lpadToTwoDigits(month)}-${lpadToTwoDigits(day)}`;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 'YYYY-MM-DD HH:mm:ss' 형식으로 format
|
|
271
|
+
* @param targetDateTime
|
|
272
|
+
* @returns string
|
|
273
|
+
*/
|
|
274
|
+
const formatDateTime = (targetDateTime) => {
|
|
275
|
+
const dateTimeValue = targetDateTime ? new Date(targetDateTime) : new Date();
|
|
276
|
+
const hour = dateTimeValue.getHours();
|
|
277
|
+
const min = dateTimeValue.getMinutes();
|
|
278
|
+
const sec = dateTimeValue.getSeconds();
|
|
279
|
+
return `${formatDate(dateTimeValue)} ${lpadToTwoDigits(hour)}:${lpadToTwoDigits(min)}:${lpadToTwoDigits(sec)}`;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 시, 분, 초를 원하는 값으로 변환
|
|
284
|
+
* @param targetDate
|
|
285
|
+
* @param hour
|
|
286
|
+
* @param min
|
|
287
|
+
* @param sec
|
|
288
|
+
* @returns {Date}
|
|
289
|
+
*/
|
|
290
|
+
const getChangedDateTime = (targetDate, hour, min, sec) => {
|
|
291
|
+
const dateTimeValue = new Date(targetDate);
|
|
292
|
+
dateTimeValue.setHours(hour);
|
|
293
|
+
dateTimeValue.setMinutes(min);
|
|
294
|
+
dateTimeValue.setSeconds(sec);
|
|
295
|
+
return dateTimeValue;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* 초기 shortcut 세팅
|
|
300
|
+
* 해당하는 날짜면 active
|
|
301
|
+
*/
|
|
302
|
+
const setActiveShortcut = () => {
|
|
303
|
+
clearShortcuts();
|
|
304
|
+
|
|
305
|
+
const isRange = ['dateRange', 'dateTimeRange'].includes(props.mode);
|
|
306
|
+
|
|
307
|
+
if (!usedShortcuts.length
|
|
308
|
+
|| (props.mode === 'dateMulti' && props.options?.multiType !== 'date')
|
|
309
|
+
|| (isRange && !mv.value.length)
|
|
310
|
+
|| (!isRange && !mv.value)
|
|
311
|
+
) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
let targetKey;
|
|
316
|
+
if (isRange) {
|
|
317
|
+
const [fromDate, toDate] = mv.value;
|
|
318
|
+
const targetShortcut = usedShortcuts.find(({ shortcutDate }) => {
|
|
319
|
+
const [sFromDate, sToDate] = shortcutDate();
|
|
320
|
+
const isCorrectFromDate = formatDate(sFromDate) === formatDate(fromDate);
|
|
321
|
+
const isCorrectToDate = formatDate(sToDate) === formatDate(toDate);
|
|
322
|
+
return isCorrectFromDate && isCorrectToDate;
|
|
323
|
+
});
|
|
324
|
+
targetKey = targetShortcut?.key;
|
|
325
|
+
} else {
|
|
326
|
+
const date = formatDate(mv.value);
|
|
327
|
+
const targetShortcut = usedShortcuts.find(({ shortcutDate }) => {
|
|
328
|
+
const sDate = formatDate(shortcutDate());
|
|
329
|
+
return sDate === formatDate(date);
|
|
330
|
+
});
|
|
331
|
+
targetKey = targetShortcut?.key;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (targetKey) {
|
|
335
|
+
activeShortcut(targetKey);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* shortcut을 클릭했을 때 이벤트
|
|
341
|
+
* @param targetKey
|
|
342
|
+
*/
|
|
343
|
+
const clickShortcut = (targetKey) => {
|
|
344
|
+
const isRange = ['dateRange', 'dateTimeRange'].includes(props.mode);
|
|
345
|
+
const targetShortcut = usedShortcuts.find(({ key }) => key === targetKey);
|
|
346
|
+
|
|
347
|
+
if (!targetShortcut) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const shortcutDate = targetShortcut.shortcutDate;
|
|
352
|
+
|
|
353
|
+
if (isRange) {
|
|
354
|
+
const [fromDate, toDate] = shortcutDate();
|
|
355
|
+
if (props.mode === 'dateTimeRange') {
|
|
356
|
+
const from = getChangedDateTime(fromDate, 0, 0, 0);
|
|
357
|
+
const to = getChangedDateTime(toDate, 0, 0, 0);
|
|
358
|
+
const [fromTimeFormat, toTimeFormat] = props.options?.timeFormat;
|
|
359
|
+
|
|
360
|
+
mv.value = [
|
|
361
|
+
getChangedValueByTimeFormat(fromTimeFormat, formatDateTime(from)),
|
|
362
|
+
getChangedValueByTimeFormat(toTimeFormat, formatDateTime(to)),
|
|
363
|
+
];
|
|
364
|
+
} else {
|
|
365
|
+
mv.value = [formatDate(fromDate), formatDate(toDate)];
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
const sDate = shortcutDate();
|
|
369
|
+
mv.value = props.mode === 'dateTime'
|
|
370
|
+
? getChangedValueByTimeFormat(
|
|
371
|
+
props.options?.timeFormat,
|
|
372
|
+
formatDateTime(getChangedDateTime(sDate, 0, 0, 0)))
|
|
373
|
+
: formatDate(sDate);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
clearShortcuts();
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
watch(
|
|
380
|
+
() => props.modelValue,
|
|
381
|
+
(curr) => {
|
|
382
|
+
setActiveShortcut();
|
|
383
|
+
if (props.mode === 'dateMulti'
|
|
384
|
+
&& props?.options?.multiType === 'date'
|
|
385
|
+
&& props?.options?.multiDayLimit > curr.length
|
|
386
|
+
) {
|
|
387
|
+
return;
|
|
388
|
+
} else if (props.mode === 'dateTime' || props.mode === 'dateTimeRange') {
|
|
389
|
+
currentValue.value = curr;
|
|
390
|
+
return;
|
|
391
|
+
} else if (props.mode === 'date') {
|
|
392
|
+
currentValue.value = curr;
|
|
393
|
+
}
|
|
394
|
+
clickOutsideDropbox();
|
|
395
|
+
},
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
usedShortcuts,
|
|
400
|
+
clickShortcut,
|
|
401
|
+
setActiveShortcut,
|
|
402
|
+
};
|
|
403
|
+
};
|
|
@@ -469,7 +469,9 @@ export default {
|
|
|
469
469
|
calculatedColumn();
|
|
470
470
|
setStore(props.rows);
|
|
471
471
|
});
|
|
472
|
-
|
|
472
|
+
const ROW_INDEX = 0;
|
|
473
|
+
const ROW_CHECK_INDEX = 1;
|
|
474
|
+
const ROW_DATA_INDEX = 2;
|
|
473
475
|
watch(
|
|
474
476
|
() => sortInfo.setSorting,
|
|
475
477
|
(value) => {
|
|
@@ -497,15 +499,18 @@ export default {
|
|
|
497
499
|
watch(
|
|
498
500
|
() => props.checked,
|
|
499
501
|
(value) => {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
+
checkInfo.checkedRows = value;
|
|
503
|
+
checkInfo.isHeaderChecked = false;
|
|
502
504
|
let store = stores.originStore;
|
|
503
505
|
if (filterInfo.isSearch && stores.searchStore) {
|
|
504
506
|
store = stores.searchStore;
|
|
505
507
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
508
|
+
store.forEach((row) => {
|
|
509
|
+
row[ROW_CHECK_INDEX] = checkInfo.checkedRows.includes(row[ROW_DATA_INDEX]);
|
|
510
|
+
});
|
|
511
|
+
if (checkInfo.checkedRows.length
|
|
512
|
+
&& store.length === checkInfo.checkedRows.length) {
|
|
513
|
+
checkInfo.isHeaderChecked = true;
|
|
509
514
|
}
|
|
510
515
|
},
|
|
511
516
|
);
|
|
@@ -560,7 +565,7 @@ export default {
|
|
|
560
565
|
let isShow = false;
|
|
561
566
|
for (let ix = 0; ix < stores.orderedColumns.length; ix++) {
|
|
562
567
|
const column = stores.orderedColumns[ix] || {};
|
|
563
|
-
let columnValue = row[
|
|
568
|
+
let columnValue = row[ROW_DATA_INDEX][ix];
|
|
564
569
|
let columnType = column.type;
|
|
565
570
|
if (columnValue) {
|
|
566
571
|
if (typeof columnValue === 'object') {
|
|
@@ -588,10 +593,10 @@ export default {
|
|
|
588
593
|
let store = stores.originStore;
|
|
589
594
|
let checkSize = checkInfo.checkedRows.length;
|
|
590
595
|
for (let ix = 0; ix < store.length; ix++) {
|
|
591
|
-
if (checkInfo.checkedIndex.has(store[ix][
|
|
592
|
-
store[ix][
|
|
596
|
+
if (checkInfo.checkedIndex.has(store[ix][ROW_INDEX])) {
|
|
597
|
+
store[ix][ROW_CHECK_INDEX] = true;
|
|
593
598
|
} else {
|
|
594
|
-
store[ix][
|
|
599
|
+
store[ix][ROW_CHECK_INDEX] = false;
|
|
595
600
|
}
|
|
596
601
|
}
|
|
597
602
|
if (filterInfo.isSearch && stores.searchStore) {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<section
|
|
3
|
+
v-resize="onResize"
|
|
4
|
+
v-observe-visibility="{
|
|
5
|
+
callback: onResize,
|
|
6
|
+
once: true,
|
|
7
|
+
}"
|
|
3
8
|
class="ev-tabs"
|
|
4
9
|
:class="{
|
|
5
10
|
closable,
|
|
@@ -85,7 +90,7 @@
|
|
|
85
90
|
import {
|
|
86
91
|
ref, reactive, computed,
|
|
87
92
|
provide, triggerRef,
|
|
88
|
-
onBeforeUpdate, nextTick,
|
|
93
|
+
onBeforeUpdate, nextTick,
|
|
89
94
|
} from 'vue';
|
|
90
95
|
|
|
91
96
|
export default {
|
|
@@ -215,16 +220,6 @@ export default {
|
|
|
215
220
|
}
|
|
216
221
|
});
|
|
217
222
|
|
|
218
|
-
// 최초 렌더링 시 El의 너비 확인
|
|
219
|
-
nextTick(() => {
|
|
220
|
-
observeListEl();
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// 화면 업데이트 시 El의 너비 확인
|
|
224
|
-
onUpdated(() => {
|
|
225
|
-
observeListEl();
|
|
226
|
-
});
|
|
227
|
-
|
|
228
223
|
/**
|
|
229
224
|
* 탭 클릭 로직
|
|
230
225
|
*/
|
|
@@ -342,6 +337,10 @@ export default {
|
|
|
342
337
|
tabCloneList.value.splice(0);
|
|
343
338
|
};
|
|
344
339
|
|
|
340
|
+
const onResize = () => {
|
|
341
|
+
observeListEl();
|
|
342
|
+
};
|
|
343
|
+
|
|
345
344
|
return {
|
|
346
345
|
mv,
|
|
347
346
|
computedTabList,
|
|
@@ -359,6 +358,8 @@ export default {
|
|
|
359
358
|
dragendTab,
|
|
360
359
|
dragSelectCls,
|
|
361
360
|
selectIdxCls,
|
|
361
|
+
|
|
362
|
+
onResize,
|
|
362
363
|
};
|
|
363
364
|
},
|
|
364
365
|
};
|
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="!!$slots.toolbar"
|
|
4
|
+
class="toolbar-wrapper"
|
|
5
|
+
:style="`width: ${gridWidth};`"
|
|
6
|
+
>
|
|
7
|
+
<!-- Toolbar -->
|
|
8
|
+
<toolbar v-if="!!$slots.toolbar" >
|
|
9
|
+
<template #toolbarWrapper>
|
|
10
|
+
<slot
|
|
11
|
+
name="toolbar"
|
|
12
|
+
:item="{
|
|
13
|
+
onSearch,
|
|
14
|
+
}"
|
|
15
|
+
>
|
|
16
|
+
</slot>
|
|
17
|
+
</template>
|
|
18
|
+
</toolbar>
|
|
19
|
+
</div>
|
|
2
20
|
<div
|
|
3
21
|
ref="grid-wrapper"
|
|
4
22
|
v-resize="onResize"
|
|
@@ -76,10 +94,10 @@
|
|
|
76
94
|
<table>
|
|
77
95
|
<tbody>
|
|
78
96
|
<tree-grid-node
|
|
79
|
-
v-for="(
|
|
97
|
+
v-for="(node, idx) in viewStore"
|
|
80
98
|
:key="idx"
|
|
81
99
|
:selected-data="selectedRow"
|
|
82
|
-
:node-data="
|
|
100
|
+
:node-data="node"
|
|
83
101
|
:use-checkbox="useCheckbox"
|
|
84
102
|
:ordered-columns="orderedColumns"
|
|
85
103
|
:expand-icon="option.expandIcon"
|
|
@@ -96,6 +114,26 @@
|
|
|
96
114
|
@click-tree-data="onRowClick"
|
|
97
115
|
@dbl-click-tree-data="onRowDblClick"
|
|
98
116
|
>
|
|
117
|
+
<!-- cell renderer -->
|
|
118
|
+
<template
|
|
119
|
+
v-for="(column, cellIndex) in orderedColumns"
|
|
120
|
+
:key="cellIndex"
|
|
121
|
+
v-slot:[getSlotName(column.field)] = "{ item }"
|
|
122
|
+
>
|
|
123
|
+
<template v-if="!!$slots[column.field]">
|
|
124
|
+
<slot
|
|
125
|
+
:name="column.field"
|
|
126
|
+
:item="{
|
|
127
|
+
data: item.data,
|
|
128
|
+
fieldName: column.field
|
|
129
|
+
}"
|
|
130
|
+
>
|
|
131
|
+
</slot>
|
|
132
|
+
</template>
|
|
133
|
+
<template v-else>
|
|
134
|
+
<span :title="node[column.field]">{{node[column.field]}}</span>
|
|
135
|
+
</template>
|
|
136
|
+
</template>
|
|
99
137
|
</tree-grid-node>
|
|
100
138
|
<tr v-if="!viewStore.length">
|
|
101
139
|
<td class="is-empty">No records</td>
|
|
@@ -126,6 +164,7 @@
|
|
|
126
164
|
<script>
|
|
127
165
|
import { reactive, toRefs, computed, watch, nextTick } from 'vue';
|
|
128
166
|
import treeGridNode from './TreeGridNode';
|
|
167
|
+
import Toolbar from './treeGrid.toolbar';
|
|
129
168
|
import {
|
|
130
169
|
commonFunctions,
|
|
131
170
|
scrollEvent,
|
|
@@ -134,12 +173,14 @@ import {
|
|
|
134
173
|
checkEvent,
|
|
135
174
|
contextMenuEvent,
|
|
136
175
|
treeEvent,
|
|
176
|
+
filterEvent,
|
|
137
177
|
} from './uses';
|
|
138
178
|
|
|
139
179
|
export default {
|
|
140
180
|
name: 'EvTreeGrid',
|
|
141
181
|
components: {
|
|
142
182
|
treeGridNode,
|
|
183
|
+
Toolbar,
|
|
143
184
|
},
|
|
144
185
|
props: {
|
|
145
186
|
columns: {
|
|
@@ -287,6 +328,10 @@ export default {
|
|
|
287
328
|
handleExpand,
|
|
288
329
|
} = treeEvent({ stores, onResize });
|
|
289
330
|
|
|
331
|
+
const {
|
|
332
|
+
onSearch,
|
|
333
|
+
} = filterEvent({ stores, getConvertValue, calculatedColumn, updateVScroll });
|
|
334
|
+
|
|
290
335
|
watch(
|
|
291
336
|
() => props.checked,
|
|
292
337
|
(value) => {
|
|
@@ -301,6 +346,12 @@ export default {
|
|
|
301
346
|
}
|
|
302
347
|
},
|
|
303
348
|
);
|
|
349
|
+
watch(
|
|
350
|
+
() => props.selected,
|
|
351
|
+
(value) => {
|
|
352
|
+
selectInfo.selectedRow = value;
|
|
353
|
+
},
|
|
354
|
+
);
|
|
304
355
|
watch(
|
|
305
356
|
() => checkInfo.useCheckbox.mode,
|
|
306
357
|
() => {
|
|
@@ -381,6 +432,7 @@ export default {
|
|
|
381
432
|
'min-width': render ? `${resizeInfo.rendererMinWidth}px;` : `${resizeInfo.minWidth}px`,
|
|
382
433
|
};
|
|
383
434
|
};
|
|
435
|
+
const getSlotName = column => `${column}Node`;
|
|
384
436
|
|
|
385
437
|
return {
|
|
386
438
|
...toRefs(styleInfo),
|
|
@@ -409,6 +461,7 @@ export default {
|
|
|
409
461
|
onCheckAll,
|
|
410
462
|
setContextMenu,
|
|
411
463
|
onContextMenu,
|
|
464
|
+
onSearch,
|
|
412
465
|
handleExpand,
|
|
413
466
|
gridStyle,
|
|
414
467
|
gridClass,
|
|
@@ -417,6 +470,7 @@ export default {
|
|
|
417
470
|
isHeaderCheckbox,
|
|
418
471
|
getColumnClass,
|
|
419
472
|
getColumnStyle,
|
|
473
|
+
getSlotName,
|
|
420
474
|
bodyStyle,
|
|
421
475
|
};
|
|
422
476
|
},
|
|
@@ -75,7 +75,16 @@
|
|
|
75
75
|
<i></i>
|
|
76
76
|
</span>
|
|
77
77
|
</span>
|
|
78
|
-
|
|
78
|
+
<!-- cell renderer -->
|
|
79
|
+
<template v-if="!!$slots[column.field + 'Node']">
|
|
80
|
+
<slot
|
|
81
|
+
:name="column.field + 'Node'"
|
|
82
|
+
:item="{
|
|
83
|
+
data: node.data,
|
|
84
|
+
}"
|
|
85
|
+
>
|
|
86
|
+
</slot>
|
|
87
|
+
</template>
|
|
79
88
|
</div>
|
|
80
89
|
</td>
|
|
81
90
|
</template>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="description tree-grid-toolbar">
|
|
3
|
+
<slot name="toolbarWrapper"></slot>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
export default {
|
|
9
|
+
name: 'EvTreeGridToolbar',
|
|
10
|
+
};
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<style lang="scss">
|
|
14
|
+
@import '../../style/index.scss';
|
|
15
|
+
.tree-grid-toolbar {
|
|
16
|
+
margin-bottom: 10px;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
}
|
|
19
|
+
.tree-grid-toolbar > .search {
|
|
20
|
+
float: right;
|
|
21
|
+
margin-right: 0;
|
|
22
|
+
}
|
|
23
|
+
.tree-grid-toolbar > .ev-button {
|
|
24
|
+
margin: 0 2px 0 2px;
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
@@ -572,29 +572,89 @@ export const treeEvent = (params) => {
|
|
|
572
572
|
node.checked = false;
|
|
573
573
|
node.index = index++;
|
|
574
574
|
node.parent = parent;
|
|
575
|
+
node.isFilter = false;
|
|
575
576
|
stores.treeStore.push(node);
|
|
576
|
-
|
|
577
577
|
if (node.children && node.children.length > 0) {
|
|
578
578
|
node.hasChild = true;
|
|
579
579
|
setTreeStore(node.children, node.level + 1, node.show && node.expand, node);
|
|
580
580
|
}
|
|
581
581
|
});
|
|
582
582
|
};
|
|
583
|
-
const setExpandNode = (children, isShow) => {
|
|
583
|
+
const setExpandNode = (children, isShow, isFilter) => {
|
|
584
584
|
children.forEach((nodeObj) => {
|
|
585
585
|
const node = nodeObj;
|
|
586
|
-
node.show = isShow;
|
|
587
|
-
|
|
586
|
+
node.show = isFilter && isShow ? node.isFilter : isShow;
|
|
588
587
|
if (node.hasChild) {
|
|
589
|
-
setExpandNode(node.children, node.show && node.expand);
|
|
588
|
+
setExpandNode(node.children, node.show && node.expand, node.isFilter);
|
|
590
589
|
}
|
|
591
590
|
});
|
|
592
591
|
};
|
|
593
592
|
const handleExpand = (node) => {
|
|
594
593
|
const data = node;
|
|
595
594
|
data.expand = !data.expand;
|
|
596
|
-
setExpandNode(data.children, data.expand);
|
|
595
|
+
setExpandNode(data.children, data.expand, data.isFilter);
|
|
597
596
|
onResize();
|
|
598
597
|
};
|
|
599
598
|
return { setTreeStore, handleExpand };
|
|
600
599
|
};
|
|
600
|
+
|
|
601
|
+
export const filterEvent = (params) => {
|
|
602
|
+
const { stores, getConvertValue, calculatedColumn, updateVScroll } = params;
|
|
603
|
+
const makeParentShow = (data) => {
|
|
604
|
+
if (!data?.parent) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
const { parent } = data;
|
|
608
|
+
parent.show = true;
|
|
609
|
+
parent.expand = true;
|
|
610
|
+
parent.isFilter = true;
|
|
611
|
+
makeParentShow(parent);
|
|
612
|
+
};
|
|
613
|
+
let timer = null;
|
|
614
|
+
const onSearch = (searchWord) => {
|
|
615
|
+
if (timer) {
|
|
616
|
+
clearTimeout(timer);
|
|
617
|
+
}
|
|
618
|
+
timer = setTimeout(() => {
|
|
619
|
+
stores.treeStore.forEach((row) => {
|
|
620
|
+
row.show = false;
|
|
621
|
+
row.isFilter = false;
|
|
622
|
+
});
|
|
623
|
+
if (searchWord) {
|
|
624
|
+
const filterStores = stores.treeStore.filter((row) => {
|
|
625
|
+
let isSameWord = false;
|
|
626
|
+
for (let ix = 0; ix < stores.orderedColumns.length; ix++) {
|
|
627
|
+
const column = stores.orderedColumns[ix] || {};
|
|
628
|
+
let columnValue = row[column.field];
|
|
629
|
+
let columnType = column.type;
|
|
630
|
+
if (columnValue) {
|
|
631
|
+
if (!columnType) {
|
|
632
|
+
columnType = 'string';
|
|
633
|
+
}
|
|
634
|
+
columnValue = getConvertValue(columnType, columnValue).toString();
|
|
635
|
+
isSameWord = columnValue.toLowerCase()
|
|
636
|
+
.includes(searchWord.toString().toLowerCase());
|
|
637
|
+
if (isSameWord) {
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return isSameWord;
|
|
643
|
+
});
|
|
644
|
+
filterStores.forEach((row) => {
|
|
645
|
+
row.show = true;
|
|
646
|
+
row.isFilter = true;
|
|
647
|
+
makeParentShow(row);
|
|
648
|
+
});
|
|
649
|
+
} else {
|
|
650
|
+
stores.treeStore.forEach((row) => {
|
|
651
|
+
row.show = true;
|
|
652
|
+
row.isFilter = false;
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
calculatedColumn();
|
|
656
|
+
updateVScroll();
|
|
657
|
+
}, 500);
|
|
658
|
+
};
|
|
659
|
+
return { onSearch };
|
|
660
|
+
};
|