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