evui 3.1.56 → 3.2.1

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.
@@ -1,4 +1,4 @@
1
- import { getCurrentInstance, nextTick } from 'vue';
1
+ import { getCurrentInstance } from 'vue';
2
2
  import { isEqual, uniqBy } from 'lodash-es';
3
3
  import { numberWithComma } from '@/common/utils';
4
4
 
@@ -368,7 +368,7 @@ export const clickEvent = (params) => {
368
368
  };
369
369
 
370
370
  export const checkEvent = (params) => {
371
- const { checkInfo, stores, filterInfo } = params;
371
+ const { checkInfo, stores } = params;
372
372
  const { emit } = getCurrentInstance();
373
373
  /**
374
374
  * row에 대한 체크 상태를 해제한다.
@@ -404,20 +404,12 @@ export const checkEvent = (params) => {
404
404
  }
405
405
  checkInfo.checkedIndex.add(row[ROW_INDEX]);
406
406
 
407
- let store = stores.originStore;
408
- let checkSize = checkInfo.checkedRows.length;
409
- if (filterInfo.isSearch && stores.searchStore) {
410
- store = stores.searchStore;
411
- checkSize = checkInfo.checkedIndex.size;
412
- }
413
- if (checkSize >= store.length) {
407
+ const store = stores.store;
408
+ const isAllChecked = store.every(d => d[ROW_CHECK_INDEX]);
409
+ if (store.length && isAllChecked) {
414
410
  checkInfo.isHeaderChecked = true;
415
411
  }
416
412
  } else {
417
- if (checkInfo.isHeaderChecked) {
418
- checkInfo.isHeaderChecked = false;
419
- }
420
-
421
413
  if (checkInfo.useCheckbox.mode === 'single') {
422
414
  checkInfo.checkedRows = [];
423
415
  checkInfo.checkedIndex.clear();
@@ -425,8 +417,8 @@ export const checkEvent = (params) => {
425
417
  checkInfo.checkedRows.splice(checkInfo.checkedRows.indexOf(row[ROW_DATA_INDEX]), 1);
426
418
  checkInfo.checkedIndex.delete(row[ROW_INDEX]);
427
419
  }
420
+ checkInfo.isHeaderChecked = false;
428
421
  }
429
-
430
422
  checkInfo.prevCheckedRow = row.slice();
431
423
  emit('update:checked', checkInfo.checkedRows);
432
424
  emit('check-row', event, row[ROW_INDEX], row[ROW_DATA_INDEX]);
@@ -437,28 +429,24 @@ export const checkEvent = (params) => {
437
429
  * @param {object} event - 이벤트 객체
438
430
  */
439
431
  const onCheckAll = (event) => {
440
- const status = checkInfo.isHeaderChecked;
441
- const checked = [];
442
- let item;
443
- let store = stores.originStore;
444
- if (filterInfo.isSearch && stores.searchStore) {
445
- store = stores.searchStore;
446
- }
447
- for (let ix = 0; ix < store.length; ix++) {
448
- item = store[ix];
449
- if (status) {
450
- checked.push(item[ROW_DATA_INDEX]);
451
- checkInfo.checkedIndex.add(item[ROW_INDEX]);
432
+ const isHeaderChecked = checkInfo.isHeaderChecked;
433
+ const store = stores.store;
434
+ store.forEach((row) => {
435
+ if (isHeaderChecked) {
436
+ if (!checkInfo.checkedRows.includes(row[ROW_DATA_INDEX])) {
437
+ checkInfo.checkedRows.push(row[ROW_DATA_INDEX]);
438
+ }
439
+ if (!checkInfo.checkedIndex.has(row[ROW_INDEX])) {
440
+ checkInfo.checkedIndex.add(row[ROW_INDEX]);
441
+ }
452
442
  } else {
453
- checkInfo.checkedIndex.clear();
443
+ checkInfo.checkedRows.splice(checkInfo.checkedRows.indexOf(row[ROW_DATA_INDEX]), 1);
444
+ checkInfo.checkedIndex.delete(row[ROW_INDEX]);
454
445
  }
455
-
456
- item[ROW_CHECK_INDEX] = status;
457
- }
458
-
459
- checkInfo.checkedRows = checked;
460
- emit('update:checked', checked);
461
- emit('check-all', event, checked);
446
+ row[ROW_CHECK_INDEX] = isHeaderChecked;
447
+ });
448
+ emit('update:checked', checkInfo.checkedRows);
449
+ emit('check-all', event, checkInfo.checkedRows);
462
450
  };
463
451
  return { onCheck, onCheckAll };
464
452
  };
@@ -475,17 +463,20 @@ export const sortEvent = (params) => {
475
463
  /**
476
464
  * sort 이벤트를 처리한다.
477
465
  *
478
- * @param {string} field - 컬럼 field
466
+ * @param {object} column - 컬럼 정보
479
467
  */
480
- const onSort = (field) => {
481
- if (sortInfo.sortField !== field) {
482
- order.orders = ['asc', 'desc', 'init'];
483
- sortInfo.sortField = field;
484
- }
485
- sortInfo.sortOrder = order.dequeue();
486
- order.enqueue(sortInfo.sortOrder);
468
+ const onSort = (column) => {
469
+ const sortable = column.sortable === undefined ? true : column.sortable;
470
+ if (sortable) {
471
+ if (sortInfo.sortField !== column?.field) {
472
+ order.orders = ['asc', 'desc', 'init'];
473
+ sortInfo.sortField = column?.field;
474
+ }
475
+ sortInfo.sortOrder = order.dequeue();
476
+ order.enqueue(sortInfo.sortOrder);
487
477
 
488
- sortInfo.isSorting = true;
478
+ sortInfo.isSorting = true;
479
+ }
489
480
  };
490
481
  /**
491
482
  * 설정값에 따라 해당 컬럼 데이터에 대해 정렬한다.
@@ -527,7 +518,14 @@ export const sortEvent = (params) => {
527
518
 
528
519
  export const filterEvent = (params) => {
529
520
  const { props } = getCurrentInstance();
530
- const { filterInfo, stores, getColumnIndex } = params;
521
+ const {
522
+ filterInfo,
523
+ stores,
524
+ checkInfo,
525
+ getColumnIndex,
526
+ getConvertValue,
527
+ updateVScroll,
528
+ } = params;
531
529
  /**
532
530
  * 해당 컬럼에 대한 필터 팝업을 보여준다.
533
531
  *
@@ -705,7 +703,57 @@ export const filterEvent = (params) => {
705
703
  stores.filterStore = uniqBy(filterStore, JSON.stringify);
706
704
  }
707
705
  };
708
- return { onClickFilter, onCloseFilterWindow, onApplyFilter, setFilter };
706
+ let timer = null;
707
+ const onSearch = (searchWord) => {
708
+ if (timer) {
709
+ clearTimeout(timer);
710
+ }
711
+ timer = setTimeout(() => {
712
+ filterInfo.isSearch = false;
713
+ filterInfo.searchWord = searchWord;
714
+ if (searchWord) {
715
+ stores.searchStore = stores.store.filter((row) => {
716
+ let isShow = false;
717
+ for (let ix = 0; ix < stores.orderedColumns.length; ix++) {
718
+ const column = stores.orderedColumns[ix] || {};
719
+ let columnValue = row[ROW_DATA_INDEX][ix];
720
+ const columnType = column.type || 'string';
721
+ if (columnValue) {
722
+ if (typeof columnValue === 'object') {
723
+ columnValue = columnValue[column.field];
724
+ }
725
+ if (!column.hide && (column?.searchable === undefined || column?.searchable)) {
726
+ columnValue = getConvertValue(columnType, columnValue).toString();
727
+ isShow = columnValue.toLowerCase().includes(searchWord.toString().toLowerCase());
728
+ if (isShow) {
729
+ break;
730
+ }
731
+ }
732
+ }
733
+ }
734
+ return isShow;
735
+ });
736
+ filterInfo.isSearch = true;
737
+ }
738
+ const store = stores.store;
739
+ let checkedCount = 0;
740
+ for (let ix = 0; ix < store.length; ix++) {
741
+ if (checkInfo.checkedIndex.has(store[ix][ROW_INDEX])) {
742
+ store[ix][ROW_CHECK_INDEX] = true;
743
+ checkedCount += 1;
744
+ } else {
745
+ store[ix][ROW_CHECK_INDEX] = false;
746
+ }
747
+ }
748
+ if (store.length && store.length === checkedCount) {
749
+ checkInfo.isHeaderChecked = true;
750
+ } else {
751
+ checkInfo.isHeaderChecked = false;
752
+ }
753
+ updateVScroll();
754
+ }, 500);
755
+ };
756
+ return { onClickFilter, onCloseFilterWindow, onApplyFilter, setFilter, onSearch };
709
757
  };
710
758
 
711
759
  export const contextMenuEvent = (params) => {
@@ -794,47 +842,40 @@ export const storeEvent = (params) => {
794
842
  * 전달된 데이터를 내부 store 및 속성에 저장한다.
795
843
  *
796
844
  * @param {array} value - row 데이터
797
- * @param {boolean} makeIndex - 인덱스 생성 유무
845
+ * @param {boolean} isMakeIndex - 인덱스 생성 유무
798
846
  */
799
- const setStore = (value, makeIndex = true) => {
800
- nextTick(() => {
801
- const store = [];
802
- let checked;
803
- let selected = false;
804
- if (makeIndex) {
805
- let hasUnChecked = false;
806
-
807
- for (let ix = 0; ix < value.length; ix++) {
808
- checked = props.checked.includes(value[ix]);
809
- if (!checked) {
810
- hasUnChecked = true;
811
- }
812
-
813
- if (!selected && isEqual(selectInfo.selectedRow, value[ix])) {
814
- selectInfo.selectedRow = value[ix];
815
- selected = true;
816
- }
817
-
818
- store.push([ix, checked, value[ix]]);
847
+ const setStore = (value, isMakeIndex = true) => {
848
+ const store = [];
849
+ let checked;
850
+ let selected = false;
851
+ if (isMakeIndex) {
852
+ let hasUnChecked = false;
853
+ for (let ix = 0; ix < value.length; ix++) {
854
+ checked = props.checked.includes(value[ix]);
855
+ if (!checked) {
856
+ hasUnChecked = true;
819
857
  }
820
-
821
- if (!selected) {
822
- selectInfo.selectedRow = [];
858
+ if (!selected && isEqual(selectInfo.selectedRow, value[ix])) {
859
+ selectInfo.selectedRow = value[ix];
860
+ selected = true;
823
861
  }
824
-
825
- checkInfo.isHeaderChecked = value.length > 0 ? !hasUnChecked : false;
826
- stores.originStore = store;
862
+ store.push([ix, checked, value[ix]]);
827
863
  }
828
- if (filterInfo.isFiltering) {
829
- setFilter();
864
+ if (!selected) {
865
+ selectInfo.selectedRow = [];
830
866
  }
831
- if (sortInfo.sortField) {
832
- setSort();
833
- }
834
- if (elementInfo.body?.clientHeight) {
835
- updateVScroll();
836
- }
837
- });
867
+ checkInfo.isHeaderChecked = value.length > 0 ? !hasUnChecked : false;
868
+ stores.originStore = store;
869
+ }
870
+ if (filterInfo.isFiltering) {
871
+ setFilter();
872
+ }
873
+ if (sortInfo.sortField) {
874
+ setSort();
875
+ }
876
+ if (elementInfo.body?.clientHeight) {
877
+ updateVScroll();
878
+ }
838
879
  };
839
880
  /**
840
881
  * 컴포넌트의 변경 데이터를 store에 업데이트한다.
@@ -61,7 +61,7 @@
61
61
  :class="{ 'on': isPasswordVisible }"
62
62
  @click="changePasswordVisible"
63
63
  >
64
- <i class="ev-icon-radio-on"/>
64
+ <i :class="isPasswordVisible ? 'ev-icon-visibility' : 'ev-icon-visibility-off'"/>
65
65
  </span>
66
66
  <span
67
67
  v-if="type === 'search'"
@@ -162,7 +162,7 @@
162
162
  </template>
163
163
 
164
164
  <script>
165
- import { reactive, toRefs, computed, watch } from 'vue';
165
+ import { reactive, toRefs, computed, watch, onMounted, onActivated } from 'vue';
166
166
  import treeGridNode from './TreeGridNode';
167
167
  import Toolbar from './treeGrid.toolbar';
168
168
  import {
@@ -246,6 +246,7 @@ export default {
246
246
  treeStore: [],
247
247
  viewStore: [],
248
248
  filterStore: [],
249
+ searchStore: computed(() => stores.treeStore.filter(item => item.isFilter)),
249
250
  treeRows: props.rows,
250
251
  showTreeStore: computed(() => stores.treeStore.filter(item => item.show)),
251
252
  orderedColumns: computed(() =>
@@ -330,20 +331,28 @@ export default {
330
331
 
331
332
  const {
332
333
  onSearch,
333
- } = filterEvent({ stores, getConvertValue, calculatedColumn, updateVScroll });
334
+ } = filterEvent({ checkInfo, stores, getConvertValue, calculatedColumn, updateVScroll });
334
335
 
336
+ onMounted(() => {
337
+ stores.treeStore = setTreeNodeStore();
338
+ });
339
+ onActivated(() => {
340
+ updateVScroll();
341
+ });
335
342
  watch(
336
343
  () => props.checked,
337
344
  (value) => {
338
- const store = stores.treeStore;
345
+ let store = stores.treeStore;
346
+ if (stores.searchStore.length > 0) {
347
+ store = stores.searchStore;
348
+ }
349
+ const isCheck = store.every(n => n.checked === true);
339
350
  checkInfo.isHeaderChecked = false;
340
351
  checkInfo.checkedRows = value;
341
352
  for (let ix = 0; ix < store.length; ix++) {
342
353
  store[ix].checked = value.includes(store[ix]);
343
354
  }
344
- if (value.length && store.length === value.length) {
345
- checkInfo.isHeaderChecked = true;
346
- }
355
+ checkInfo.isHeaderChecked = isCheck;
347
356
  },
348
357
  );
349
358
  watch(
@@ -359,8 +368,6 @@ export default {
359
368
  checkInfo.isHeaderChecked = false;
360
369
  },
361
370
  );
362
- stores.treeStore = setTreeNodeStore();
363
-
364
371
  watch(
365
372
  () => props.rows,
366
373
  (newData) => {
@@ -418,11 +418,14 @@ export const checkEvent = (params) => {
418
418
  * @param {array} rowData - row 데이터
419
419
  */
420
420
  const onCheck = (event, rowData) => {
421
+ let store = stores.treeStore;
422
+ if (stores.searchStore.length > 0) {
423
+ store = stores.searchStore;
424
+ }
421
425
  const isSingleMode = () => checkInfo.useCheckbox.mode === 'single';
422
- const checkedHeader = (store) => {
423
- if (checkInfo.checkedRows.length === store.length) {
424
- checkInfo.isHeaderChecked = true;
425
- }
426
+ const checkedHeader = (checkStore) => {
427
+ const isCheck = checkStore.every(n => n.checked === true);
428
+ checkInfo.isHeaderChecked = isCheck;
426
429
  };
427
430
  const unCheckedHeader = () => {
428
431
  if (checkInfo.isHeaderChecked) {
@@ -455,7 +458,7 @@ export const checkEvent = (params) => {
455
458
  onSingleMode();
456
459
  if (rowData.checked) {
457
460
  addCheckedRow(rowData);
458
- checkedHeader(stores.treeStore);
461
+ checkedHeader(store);
459
462
  } else {
460
463
  unCheckedHeader();
461
464
  removeCheckedRow(rowData);
@@ -472,10 +475,13 @@ export const checkEvent = (params) => {
472
475
  * @param {object} event - 이벤트 객체
473
476
  */
474
477
  const onCheckAll = (event) => {
475
- const store = stores.treeStore;
476
478
  const status = checkInfo.isHeaderChecked;
477
479
  const checked = [];
478
480
  let item;
481
+ let store = stores.treeStore;
482
+ if (status && stores.searchStore.length > 0) {
483
+ store = stores.searchStore;
484
+ }
479
485
  for (let ix = 0; ix < store.length; ix++) {
480
486
  item = store[ix];
481
487
  if (status) {
@@ -484,6 +490,11 @@ export const checkEvent = (params) => {
484
490
  item.checked = status;
485
491
  }
486
492
  checkInfo.checkedRows = checked;
493
+ if (stores.searchStore.length > 0) {
494
+ store.forEach((node) => {
495
+ onCheckChildren(node);
496
+ });
497
+ }
487
498
  emit('update:checked', checked);
488
499
  emit('check-all', event, checked);
489
500
  };
@@ -637,7 +648,7 @@ export const treeEvent = (params) => {
637
648
  };
638
649
 
639
650
  export const filterEvent = (params) => {
640
- const { stores, getConvertValue, calculatedColumn, updateVScroll } = params;
651
+ const { checkInfo, stores, getConvertValue, calculatedColumn, updateVScroll } = params;
641
652
  const makeParentShow = (data) => {
642
653
  if (!data?.parent) {
643
654
  return;
@@ -654,26 +665,29 @@ export const filterEvent = (params) => {
654
665
  clearTimeout(timer);
655
666
  }
656
667
  timer = setTimeout(() => {
657
- stores.treeStore.forEach((row) => {
668
+ let store = stores.treeStore;
669
+ store.forEach((row) => {
658
670
  row.show = false;
659
671
  row.isFilter = false;
660
672
  });
661
673
  if (searchWord) {
662
- const filterStores = stores.treeStore.filter((row) => {
674
+ const filterStores = store.filter((row) => {
663
675
  let isSameWord = false;
664
676
  for (let ix = 0; ix < stores.orderedColumns.length; ix++) {
665
677
  const column = stores.orderedColumns[ix] || {};
666
678
  let columnValue = row[column.field];
667
679
  let columnType = column.type;
668
680
  if (columnValue) {
669
- if (!columnType) {
670
- columnType = 'string';
671
- }
672
- columnValue = getConvertValue(columnType, columnValue).toString();
673
- isSameWord = columnValue.toLowerCase()
674
- .includes(searchWord.toString().toLowerCase());
675
- if (isSameWord) {
676
- break;
681
+ if (!column.hide && (column?.searchable === undefined || column?.searchable)) {
682
+ if (!columnType) {
683
+ columnType = 'string';
684
+ }
685
+ columnValue = getConvertValue(columnType, columnValue).toString();
686
+ isSameWord = columnValue.toLowerCase()
687
+ .includes(searchWord.toString().toLowerCase());
688
+ if (isSameWord) {
689
+ break;
690
+ }
677
691
  }
678
692
  }
679
693
  }
@@ -685,11 +699,16 @@ export const filterEvent = (params) => {
685
699
  makeParentShow(row);
686
700
  });
687
701
  } else {
688
- stores.treeStore.forEach((row) => {
702
+ store.forEach((row) => {
689
703
  row.show = true;
690
704
  row.isFilter = false;
691
705
  });
692
706
  }
707
+ if (stores.searchStore.length > 0) {
708
+ store = stores.searchStore;
709
+ }
710
+ const isCheck = store.every(n => n.checked === true);
711
+ checkInfo.isHeaderChecked = isCheck;
693
712
  calculatedColumn();
694
713
  updateVScroll();
695
714
  }, 500);
@@ -27,6 +27,7 @@
27
27
  }"
28
28
  @mousedown="startDrag"
29
29
  @mousemove="moveMouse"
30
+ @click="setFocus"
30
31
  >
31
32
  <div
32
33
  v-if="$slots.header || iconClass || title"
@@ -84,11 +85,17 @@
84
85
  </template>
85
86
 
86
87
  <script>
87
- import { useEscKeydownEvent, useModel, useMouseEvent } from './uses';
88
+ import { useEscCloseAndFocusable, useModel, useMouseEvent } from './uses';
88
89
 
89
90
  export default {
90
91
  name: 'EvWindow',
91
92
  props: {
93
+ style: {
94
+ type: Object,
95
+ default() {
96
+ return {};
97
+ },
98
+ },
92
99
  visible: {
93
100
  type: Boolean,
94
101
  default: false,
@@ -153,6 +160,10 @@ export default {
153
160
  type: Boolean,
154
161
  default: false,
155
162
  },
163
+ focusable: {
164
+ type: Boolean,
165
+ default: false,
166
+ },
156
167
  },
157
168
  emits: [
158
169
  'update:visible',
@@ -187,7 +198,7 @@ export default {
187
198
  removeUnit,
188
199
  });
189
200
 
190
- useEscKeydownEvent({ closeWin, windowRef });
201
+ const { setFocus } = useEscCloseAndFocusable({ closeWin, windowRef });
191
202
 
192
203
  return {
193
204
  windowRef,
@@ -200,6 +211,8 @@ export default {
200
211
  startDrag,
201
212
  moveMouse,
202
213
  clickExpandBtn,
214
+
215
+ setFocus,
203
216
  };
204
217
  },
205
218
  };