vben
2021-08-24 56a966cfbf8db5b29a42185f0f25a0e800c30dbb
提交 | 用户 | age
116a1f 1 <template>
V 2   <Tooltip placement="top">
3     <template #title>
4       <span>{{ t('component.table.settingColumn') }}</span>
5     </template>
6     <Popover
7       placement="bottomLeft"
8       trigger="click"
9       @visibleChange="handleVisibleChange"
10       :overlayClassName="`${prefixCls}__cloumn-list`"
dce3fb 11       :getPopupContainer="getPopupContainer"
116a1f 12     >
V 13       <template #title>
14         <div :class="`${prefixCls}__popover-title`">
15           <Checkbox
16             :indeterminate="indeterminate"
17             v-model:checked="checkAll"
18             @change="onCheckAllChange"
19           >
20             {{ t('component.table.settingColumnShow') }}
21           </Checkbox>
22
23           <Checkbox v-model:checked="checkIndex" @change="handleIndexCheckChange">
24             {{ t('component.table.settingIndexColumnShow') }}
25           </Checkbox>
26
27           <Checkbox
28             v-model:checked="checkSelect"
29             @change="handleSelectCheckChange"
30             :disabled="!defaultRowSelection"
31           >
32             {{ t('component.table.settingSelectColumnShow') }}
33           </Checkbox>
34
35           <a-button size="small" type="link" @click="reset">
efbde0 36             {{ t('common.resetText') }}
116a1f 37           </a-button>
V 38         </div>
39       </template>
40
41       <template #content>
42         <ScrollContainer>
43           <CheckboxGroup v-model:value="checkedList" @change="onChange" ref="columnListRef">
44             <template v-for="item in plainOptions" :key="item.value">
45               <div :class="`${prefixCls}__check-item`">
46                 <DragOutlined class="table-coulmn-drag-icon" />
9edc28 47                 <Checkbox :value="item.value">
V 48                   {{ item.label }}
49                 </Checkbox>
116a1f 50
dce3fb 51                 <Tooltip
52                   placement="bottomLeft"
53                   :mouseLeaveDelay="0.4"
54                   :getPopupContainer="getPopupContainer"
55                 >
9edc28 56                   <template #title>
V 57                     {{ t('component.table.settingFixedLeft') }}
58                   </template>
116a1f 59                   <Icon
V 60                     icon="line-md:arrow-align-left"
61                     :class="[
62                       `${prefixCls}__fixed-left`,
63                       {
64                         active: item.fixed === 'left',
65                         disabled: !checkedList.includes(item.value),
66                       },
67                     ]"
68                     @click="handleColumnFixed(item, 'left')"
69                   />
70                 </Tooltip>
71                 <Divider type="vertical" />
dce3fb 72                 <Tooltip
73                   placement="bottomLeft"
74                   :mouseLeaveDelay="0.4"
75                   :getPopupContainer="getPopupContainer"
76                 >
9edc28 77                   <template #title>
V 78                     {{ t('component.table.settingFixedRight') }}
79                   </template>
116a1f 80                   <Icon
V 81                     icon="line-md:arrow-align-left"
82                     :class="[
83                       `${prefixCls}__fixed-right`,
84                       {
85                         active: item.fixed === 'right',
86                         disabled: !checkedList.includes(item.value),
87                       },
88                     ]"
89                     @click="handleColumnFixed(item, 'right')"
90                   />
91                 </Tooltip>
92               </div>
93             </template>
94           </CheckboxGroup>
95         </ScrollContainer>
96       </template>
97       <SettingOutlined />
98     </Popover>
99   </Tooltip>
100 </template>
101 <script lang="ts">
e90646 102   import type { BasicColumn, ColumnChangeParam } from '../../types/table';
116a1f 103   import {
V 104     defineComponent,
105     ref,
106     reactive,
107     toRefs,
108     watchEffect,
109     nextTick,
110     unref,
111     computed,
112   } from 'vue';
113   import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue';
114   import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue';
115   import { Icon } from '/@/components/Icon';
116   import { ScrollContainer } from '/@/components/Container';
117   import { useI18n } from '/@/hooks/web/useI18n';
118   import { useTableContext } from '../../hooks/useTableContext';
119   import { useDesign } from '/@/hooks/web/useDesign';
120   import { useSortable } from '/@/hooks/web/useSortable';
dce3fb 121   import { isFunction, isNullAndUnDef } from '/@/utils/is';
122   import { getPopupContainer as getParentContainer } from '/@/utils';
c3096e 123   import { omit } from 'lodash-es';
116a1f 124
V 125   interface State {
126     checkAll: boolean;
127     checkedList: string[];
128     defaultCheckList: string[];
129   }
130
131   interface Options {
132     label: string;
133     value: string;
134     fixed?: boolean | 'left' | 'right';
135   }
136
137   export default defineComponent({
138     name: 'ColumnSetting',
139     components: {
140       SettingOutlined,
141       Popover,
142       Tooltip,
143       Checkbox,
144       CheckboxGroup: Checkbox.Group,
145       DragOutlined,
146       ScrollContainer,
147       Divider,
148       Icon,
149     },
125a7d 150     emits: ['columns-change'],
116a1f 151
dce3fb 152     setup(_, { emit, attrs }) {
116a1f 153       const { t } = useI18n();
V 154       const table = useTableContext();
155
c3096e 156       const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys');
116a1f 157       let inited = false;
V 158
159       const cachePlainOptions = ref<Options[]>([]);
160       const plainOptions = ref<Options[]>([]);
161
162       const plainSortOptions = ref<Options[]>([]);
163
164       const columnListRef = ref<ComponentRef>(null);
165
166       const state = reactive<State>({
167         checkAll: true,
168         checkedList: [],
169         defaultCheckList: [],
170       });
171
172       const checkIndex = ref(false);
173       const checkSelect = ref(false);
174
175       const { prefixCls } = useDesign('basic-column-setting');
176
177       const getValues = computed(() => {
178         return unref(table?.getBindValues) || {};
179       });
180
181       watchEffect(() => {
182         const columns = table.getColumns();
183         if (columns.length) {
184           init();
185         }
186       });
187
188       watchEffect(() => {
189         const values = unref(getValues);
190         checkIndex.value = !!values.showIndexColumn;
191         checkSelect.value = !!values.rowSelection;
192       });
193
194       function getColumns() {
195         const ret: Options[] = [];
196         table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {
197           ret.push({
9c2f3f 198             label: (item.title as string) || (item.customTitle as string),
116a1f 199             value: (item.dataIndex || item.title) as string,
V 200             ...item,
201           });
202         });
203         return ret;
204       }
205
206       function init() {
207         const columns = getColumns();
208
209         const checkList = table
4fd205 210           .getColumns({ ignoreAction: true })
116a1f 211           .map((item) => {
V 212             if (item.defaultHidden) {
213               return '';
214             }
215             return item.dataIndex || item.title;
216           })
217           .filter(Boolean) as string[];
218
219         if (!plainOptions.value.length) {
220           plainOptions.value = columns;
221           plainSortOptions.value = columns;
222           cachePlainOptions.value = columns;
223           state.defaultCheckList = checkList;
224         } else {
a2c89d 225           // const fixedColumns = columns.filter((item) =>
V 226           //   Reflect.has(item, 'fixed')
227           // ) as BasicColumn[];
116a1f 228
V 229           unref(plainOptions).forEach((item: BasicColumn) => {
a2c89d 230             const findItem = columns.find((col: BasicColumn) => col.dataIndex === item.dataIndex);
116a1f 231             if (findItem) {
V 232               item.fixed = findItem.fixed;
233             }
234           });
235         }
236         state.checkedList = checkList;
237       }
238
239       // checkAll change
240       function onCheckAllChange(e: ChangeEvent) {
241         const checkList = plainOptions.value.map((item) => item.value);
242         if (e.target.checked) {
243           state.checkedList = checkList;
125a7d 244           setColumns(checkList);
116a1f 245         } else {
V 246           state.checkedList = [];
125a7d 247           setColumns([]);
116a1f 248         }
V 249       }
250
495b1d 251       const indeterminate = computed(() => {
252         const len = plainOptions.value.length;
253         let checkdedLen = state.checkedList.length;
4fd205 254         unref(checkIndex) && checkdedLen--;
495b1d 255         return checkdedLen > 0 && checkdedLen < len;
256       });
257
116a1f 258       // Trigger when check/uncheck a column
V 259       function onChange(checkedList: string[]) {
260         const len = plainOptions.value.length;
261         state.checkAll = checkedList.length === len;
262
263         const sortList = unref(plainSortOptions).map((item) => item.value);
264         checkedList.sort((prev, next) => {
265           return sortList.indexOf(prev) - sortList.indexOf(next);
266         });
125a7d 267         setColumns(checkedList);
116a1f 268       }
V 269
270       // reset columns
271       function reset() {
272         state.checkedList = [...state.defaultCheckList];
273         state.checkAll = true;
274         plainOptions.value = unref(cachePlainOptions);
275         plainSortOptions.value = unref(cachePlainOptions);
125a7d 276         setColumns(table.getCacheColumns());
116a1f 277       }
V 278
279       // Open the pop-up window for drag and drop initialization
280       function handleVisibleChange() {
281         if (inited) return;
282         nextTick(() => {
283           const columnListEl = unref(columnListRef);
284           if (!columnListEl) return;
9035fd 285           const el = columnListEl.$el as any;
116a1f 286           if (!el) return;
V 287           // Drag and drop sort
288           const { initSortable } = useSortable(el, {
289             handle: '.table-coulmn-drag-icon ',
290             onEnd: (evt) => {
291               const { oldIndex, newIndex } = evt;
292               if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) {
293                 return;
294               }
295               // Sort column
296               const columns = getColumns();
297
298               if (oldIndex > newIndex) {
299                 columns.splice(newIndex, 0, columns[oldIndex]);
300                 columns.splice(oldIndex + 1, 1);
301               } else {
302                 columns.splice(newIndex + 1, 0, columns[oldIndex]);
303                 columns.splice(oldIndex, 1);
304               }
305
306               plainSortOptions.value = columns;
307               plainOptions.value = columns;
125a7d 308               setColumns(columns);
116a1f 309             },
V 310           });
311           initSortable();
312           inited = true;
313         });
314       }
315
316       // Control whether the serial number column is displayed
317       function handleIndexCheckChange(e: ChangeEvent) {
318         table.setProps({
319           showIndexColumn: e.target.checked,
320         });
321       }
322
323       // Control whether the check box is displayed
324       function handleSelectCheckChange(e: ChangeEvent) {
325         table.setProps({
326           rowSelection: e.target.checked ? defaultRowSelection : undefined,
327         });
328       }
329
330       function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
331         if (!state.checkedList.includes(item.dataIndex as string)) return;
332
333         const columns = getColumns() as BasicColumn[];
334         const isFixed = item.fixed === fixed ? false : fixed;
335         const index = columns.findIndex((col) => col.dataIndex === item.dataIndex);
336         if (index !== -1) {
337           columns[index].fixed = isFixed;
338         }
339         item.fixed = isFixed;
340
341         if (isFixed && !item.width) {
342           item.width = 100;
343         }
c96002 344         table.setCacheColumnsByField?.(item.dataIndex, { fixed: isFixed });
125a7d 345         setColumns(columns);
346       }
347
348       function setColumns(columns: BasicColumn[] | string[]) {
116a1f 349         table.setColumns(columns);
125a7d 350         const data: ColumnChangeParam[] = unref(plainOptions).map((col) => {
351           const visible =
352             columns.findIndex(
353               (c: BasicColumn | string) =>
56a966 354                 c === col.value || (typeof c !== 'string' && c.dataIndex === col.value),
125a7d 355             ) !== -1;
356           return { dataIndex: col.value, fixed: col.fixed, visible };
357         });
358
359         emit('columns-change', data);
116a1f 360       }
V 361
dce3fb 362       function getPopupContainer() {
363         return isFunction(attrs.getPopupContainer)
364           ? attrs.getPopupContainer()
365           : getParentContainer();
366       }
367
116a1f 368       return {
V 369         t,
370         ...toRefs(state),
495b1d 371         indeterminate,
116a1f 372         onCheckAllChange,
V 373         onChange,
374         plainOptions,
375         reset,
376         prefixCls,
377         columnListRef,
378         handleVisibleChange,
379         checkIndex,
380         checkSelect,
381         handleIndexCheckChange,
382         handleSelectCheckChange,
383         defaultRowSelection,
384         handleColumnFixed,
385         getPopupContainer,
386       };
387     },
388   });
389 </script>
390 <style lang="less">
391   @prefix-cls: ~'@{namespace}-basic-column-setting';
392
393   .table-coulmn-drag-icon {
394     margin: 0 5px;
395     cursor: move;
396   }
397
398   .@{prefix-cls} {
399     &__popover-title {
400       position: relative;
401       display: flex;
402       align-items: center;
403       justify-content: space-between;
404     }
405
406     &__check-item {
407       display: flex;
408       align-items: center;
409       min-width: 100%;
410       padding: 4px 16px 8px 0;
411
412       .ant-checkbox-wrapper {
413         width: 100%;
414
415         &:hover {
416           color: @primary-color;
417         }
418       }
419     }
420
421     &__fixed-left,
422     &__fixed-right {
423       color: rgba(0, 0, 0, 0.45);
424       cursor: pointer;
425
426       &.active,
427       &:hover {
428         color: @primary-color;
429       }
430
431       &.disabled {
432         color: @disabled-color;
433         cursor: not-allowed;
434       }
435     }
436
437     &__fixed-right {
438       transform: rotate(180deg);
439     }
440
441     &__cloumn-list {
442       svg {
443         width: 1em !important;
444         height: 1em !important;
445       }
446
447       .ant-popover-inner-content {
448         // max-height: 360px;
449         padding-right: 0;
450         padding-left: 0;
451         // overflow: auto;
452       }
453
454       .ant-checkbox-group {
455         width: 100%;
456         min-width: 260px;
457         // flex-wrap: wrap;
458       }
459
0e7c57 460       .scrollbar {
116a1f 461         height: 220px;
V 462       }
463     }
464   }
465 </style>