Browse Source

feat: ColumnSetting and SizeSetting persist (#3398)

xachary 1 year ago
parent
commit
f4df2d5a4b

+ 458 - 245
src/components/Table/src/components/settings/ColumnSetting.vue

@@ -6,7 +6,7 @@
     <Popover
       placement="bottomLeft"
       trigger="click"
-      @open-change="handleVisibleChange"
+      @open-change="onOpenChange"
       :overlayClassName="`${prefixCls}__column-list`"
       :getPopupContainer="getPopupContainer"
     >
@@ -14,25 +14,25 @@
         <div :class="`${prefixCls}__popover-title`">
           <Checkbox
             :indeterminate="indeterminate"
-            v-model:checked="state.checkAll"
-            @change="onCheckAllChange"
+            v-model:checked="isColumnAllSelected"
+            @change="onColumnAllSelectChange"
           >
             {{ t('component.table.settingColumnShow') }}
           </Checkbox>
 
-          <Checkbox v-model:checked="checkIndex" @change="handleIndexCheckChange">
+          <Checkbox v-model:checked="isIndexColumnShow" @change="onIndexColumnShowChange">
             {{ t('component.table.settingIndexColumnShow') }}
           </Checkbox>
-
+          <!-- 设置了 rowSelection 才出现 -->
           <Checkbox
-            v-model:checked="checkSelect"
-            @change="handleSelectCheckChange"
-            :disabled="!defaultRowSelection"
+            v-model:checked="isRowSelectionShow"
+            @change="onRowSelectionShowChange"
+            v-if="defaultIsRowSelectionShow"
           >
             {{ t('component.table.settingSelectColumnShow') }}
           </Checkbox>
 
-          <a-button size="small" type="link" @click="reset">
+          <a-button size="small" type="link" @click="onReset">
             {{ t('common.resetText') }}
           </a-button>
         </div>
@@ -40,12 +40,12 @@
 
       <template #content>
         <ScrollContainer>
-          <Checkbox.Group v-model:value="state.checkedList" @change="onChange" ref="columnListRef">
-            <template v-for="item in plainOptions" :key="item.value">
-              <div :class="`${prefixCls}__check-item`" v-if="!('ifShow' in item && !item.ifShow)">
+          <Checkbox.Group v-model:value="columnCheckedOptions" ref="columnOptionsRef">
+            <template v-for="opt in columnOptions" :key="opt.value">
+              <div :class="`${prefixCls}__check-item`" :data-no="opt.value">
                 <DragOutlined class="table-column-drag-icon" />
-                <Checkbox :value="item.value">
-                  {{ item.label }}
+                <Checkbox :value="opt.value">
+                  {{ opt.label }}
                 </Checkbox>
 
                 <Tooltip
@@ -61,11 +61,11 @@
                     :class="[
                       `${prefixCls}__fixed-left`,
                       {
-                        active: item.fixed === 'left',
-                        disabled: !state.checkedList.includes(item.value),
+                        active: opt.fixed === 'left',
+                        disabled: opt.value ? !columnCheckedOptions.includes(opt.value) : true,
                       },
                     ]"
-                    @click="handleColumnFixed(item, 'left')"
+                    @click="onColumnFixedChange(opt, 'left')"
                   />
                 </Tooltip>
                 <Divider type="vertical" />
@@ -82,11 +82,11 @@
                     :class="[
                       `${prefixCls}__fixed-right`,
                       {
-                        active: item.fixed === 'right',
-                        disabled: !state.checkedList.includes(item.value),
+                        active: opt.fixed === 'right',
+                        disabled: opt.value ? !columnCheckedOptions.includes(opt.value) : true,
                       },
                     ]"
-                    @click="handleColumnFixed(item, 'right')"
+                    @click="onColumnFixedChange(opt, 'right')"
                   />
                 </Tooltip>
               </div>
@@ -99,302 +99,515 @@
   </Tooltip>
 </template>
 <script lang="ts" setup>
-  import type { BasicColumn, BasicTableProps, ColumnChangeParam } from '../../types/table';
-  import { ref, reactive, watchEffect, nextTick, unref, computed, useAttrs } from 'vue';
+  import type { BasicColumn, ColumnOptionsType, ColumnChangeParam } from '../../types/table';
+  import { ref, nextTick, unref, computed, useAttrs, watch, onMounted } from 'vue';
   import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue';
-  import type {
-    CheckboxChangeEvent,
-    CheckboxValueType,
-  } from 'ant-design-vue/lib/checkbox/interface';
+  import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface';
   import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue';
   import Icon from '@/components/Icon/Icon.vue';
   import { ScrollContainer } from '@/components/Container';
   import { useI18n } from '@/hooks/web/useI18n';
   import { useTableContext } from '../../hooks/useTableContext';
   import { useDesign } from '@/hooks/web/useDesign';
-  // import { useSortable } from '@/hooks/web/useSortable';
   import { isFunction, isNil } from '@/utils/is';
   import { getPopupContainer as getParentContainer } from '@/utils';
-  import { cloneDeep, omit } from 'lodash-es';
+  import { cloneDeep } from 'lodash-es';
   import Sortablejs from 'sortablejs';
   import type Sortable from 'sortablejs';
 
-  interface State {
-    checkAll: boolean;
-    isInit?: boolean;
-    checkedList: string[];
-    defaultCheckList: string[];
-  }
+  // 列表设置缓存
+  import { useTableSettingStore } from '@/store/modules/tableSetting';
+  import { useRoute } from 'vue-router';
+  import { TableRowSelection } from '@/components/Table/src/types/table';
 
-  interface Options {
-    label: string;
-    value: string;
-    fixed?: boolean | 'left' | 'right';
-  }
+  const tableSettingStore = useTableSettingStore();
 
   defineOptions({ name: 'ColumnSetting' });
-
   const emit = defineEmits(['columns-change']);
 
-  const attrs = useAttrs();
-  const { t } = useI18n();
-  const table = useTableContext();
-
-  const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys');
-  let inited = false;
-  // 是否当前的setColumns触发的
-  let isSetColumnsFromThis = false;
-  // 是否当前组件触发的setProps
-  let isSetPropsFromThis = false;
-
-  const cachePlainOptions = ref<Options[]>([]);
-  const plainOptions = ref<Options[] | any>([]);
+  const route = useRoute();
 
-  const plainSortOptions = ref<Options[]>([]);
+  const { t } = useI18n();
+  const { prefixCls } = useDesign('basic-column-setting');
 
-  const columnListRef = ref(null);
+  const attrs = useAttrs();
+  const table = useTableContext();
 
-  const state = reactive<State>({
-    checkAll: true,
-    checkedList: [],
-    defaultCheckList: [],
+  const getPopupContainer = () => {
+    return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer();
+  };
+
+  // 是否已经从缓存恢复
+  let isRestored = false;
+  let isInnerChange = false;
+
+  // 列可选项
+  const columnOptions = ref<ColumnOptionsType[]>([]);
+  const columnOptionsRef = ref(null);
+  // 已选列
+  const columnCheckedOptions = ref<string[]>([]);
+  // 已选变化
+  watch(columnCheckedOptions, () => {
+    // 恢复缓存后生效
+    if (isRestored) {
+      // 显示
+      columnOptions.value
+        .filter((o) => columnCheckedOptions.value.includes(o.value))
+        .forEach((o) => {
+          o.column.defaultHidden = false;
+        });
+      // 隐藏
+      columnOptions.value
+        .filter((o) => !columnCheckedOptions.value.includes(o.value))
+        .forEach((o) => {
+          o.column.defaultHidden = true;
+          o.fixed = undefined;
+        });
+      // 从 列可选项 更新 全选状态
+      isColumnAllSelectedUpdate();
+
+      // 列表列更新
+      tableColumnsUpdate();
+      // 更新列缓存
+      columnOptionsSave();
+    }
   });
-  /** 缓存初始化props */
-  let cacheTableProps: Partial<BasicTableProps<any>> = {};
-  const checkIndex = ref(false);
-  const checkSelect = ref(false);
 
-  const { prefixCls } = useDesign('basic-column-setting');
+  // 全选
+  const isColumnAllSelected = ref<boolean>(false);
+  const onColumnAllSelectChange = () => {
+    if (columnCheckedOptions.value.length < columnOptions.value.length) {
+      columnCheckedOptions.value = columnOptions.value.map((o) => o.value);
+    } else {
+      columnCheckedOptions.value = [];
+    }
+  };
 
-  const getValues = computed(() => {
-    return unref(table?.getBindValues) || {};
+  // 半选状态
+  const indeterminate = computed(() => {
+    return (
+      columnCheckedOptions.value.length > 0 &&
+      columnCheckedOptions.value.length < columnOptions.value.length
+    );
   });
 
-  watchEffect(() => {
-    const columns = table.getColumns();
-    setTimeout(() => {
-      if (isSetColumnsFromThis) {
-        isSetColumnsFromThis = false;
-      } else if (columns.length) {
-        init();
+  // 是否显示序号列
+  const isIndexColumnShow = ref<boolean>(false);
+  // 序号列更新
+  const onIndexColumnShowChange = (e: CheckboxChangeEvent) => {
+    // 更新 showIndexColumn
+    showIndexColumnUpdate(e.target.checked);
+    // 更新 showIndexColumn 缓存
+    tableSettingStore.setShowIndexColumn(e.target.checked);
+    // 从无到有需要处理
+    if (e.target.checked) {
+      const columns = cloneDeep(table?.getColumns());
+      const idx = columns.findIndex((o) => o.flag === 'INDEX');
+      // 找到序号列
+      if (idx > -1) {
+        const cache = columns[idx];
+        // 强制左fix
+        cache.fixed = 'left';
+        // 强制移动到 第一/选择列后
+        columns.splice(idx, 1);
+        columns.splice(0, 0, cache);
+        // 设置列表列
+        tableColumnsSet(columns);
+      }
+    }
+  };
+
+  // 是否显示选择列
+  const isRowSelectionShow = ref<boolean>(false);
+  // 选择列更新
+  const onRowSelectionShowChange = (e: CheckboxChangeEvent) => {
+    // 更新 showRowSelection
+    showRowSelectionUpdate(e.target.checked);
+    // 更新 showRowSelection 缓存
+    tableSettingStore.setShowRowSelection(e.target.checked);
+  };
+
+  // 更新列缓存
+  const columnOptionsSave = () => {
+    if (typeof route.name === 'string') {
+      // 按路由 name 作为缓存的key(若一个路由内存在多个表格,需自行调整缓存key来源)
+      tableSettingStore.setColumns(route.name, columnOptions.value);
+    }
+  };
+
+  // 重置
+  const onReset = () => {
+    // 重置默认值
+    isIndexColumnShow.value = defaultIsIndexColumnShow;
+    // 序号列更新
+    onIndexColumnShowChange({
+      target: { checked: defaultIsIndexColumnShow },
+    } as CheckboxChangeEvent);
+    // 重置默认值
+    isRowSelectionShow.value = defaultIsRowSelectionShow;
+    // 选择列更新
+    onRowSelectionShowChange({
+      target: { checked: defaultIsRowSelectionShow },
+    } as CheckboxChangeEvent);
+    // 重置默认值
+    columnOptions.value = cloneDeep(defaultColumnOptions);
+    // 重置排序
+    sortableOrder = defaultSortableOrder;
+    // 排序
+    sortable?.sort(defaultSortableOrder);
+    // 更新表单状态
+    formUpdate();
+  };
+
+  // 设置列的 fixed
+  const onColumnFixedChange = (opt: ColumnOptionsType, type: 'left' | 'right') => {
+    if (type === 'left') {
+      if (!opt.fixed || opt.fixed === 'right') {
+        opt.fixed = 'left';
+      } else {
+        opt.fixed = undefined;
+      }
+    } else if (type === 'right') {
+      if (!opt.fixed || opt.fixed === 'left') {
+        opt.fixed = 'right';
+      } else {
+        opt.fixed = undefined;
       }
-    }, 0);
-  });
-
-  watchEffect(() => {
-    const values = unref(getValues);
-    if (isSetPropsFromThis) {
-      isSetPropsFromThis = false;
-    } else {
-      cacheTableProps = cloneDeep(values);
     }
-    checkIndex.value = !!values.showIndexColumn;
-    checkSelect.value = !!values.rowSelection;
-  });
 
-  function getColumns() {
-    const ret: Options[] = [];
-    table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {
-      ret.push({
-        label: (item.title as string) || (item.customTitle as string),
-        value: (item.dataIndex || item.title) as string,
-        ...item,
-      });
-    });
-    return ret;
-  }
+    // 列表列更新
+    tableColumnsUpdate();
+    // 更新列缓存
+    columnOptionsSave();
+  };
 
-  async function init(isReset = false) {
+  // 沿用逻辑
+  const sortableFix = async () => {
     // Sortablejs存在bug,不知道在哪个步骤中会向el append了一个childNode,因此这里先清空childNode
     // 有可能复现上述问题的操作:拖拽一个元素,快速的上下移动,最后放到最后的位置中松手
-    plainOptions.value = [];
-    const columnListEl = unref(columnListRef);
-    if (columnListEl && (columnListEl as any).$el) {
-      const el = (columnListEl as any).$el as Element;
+    if (columnOptionsRef.value) {
+      const el = (columnOptionsRef.value as InstanceType<typeof Checkbox.Group>).$el;
       Array.from(el.children).forEach((item) => el.removeChild(item));
     }
     await nextTick();
-    const columns = isReset ? cloneDeep(cachePlainOptions.value) : getColumns();
-
-    const checkList = table
-      .getColumns({ ignoreAction: true, ignoreIndex: true })
-      .map((item) => {
-        if (item.defaultHidden) {
-          return '';
+  };
+
+  // 列是否显示逻辑
+  const columnIfShow = (column?: Partial<Omit<BasicColumn, 'children'>>) => {
+    if (column) {
+      if ('ifShow' in column) {
+        if (typeof column.ifShow === 'boolean') {
+          return column.ifShow;
+        } else if (column.ifShow) {
+          return column.ifShow(column);
         }
-        return item.dataIndex || item.title;
-      })
-      .filter(Boolean) as string[];
-    plainOptions.value = columns;
-    plainSortOptions.value = columns;
-    // 更新缓存配置
-    table.setCacheColumns?.(columns);
-    !isReset && (cachePlainOptions.value = cloneDeep(columns));
-    state.defaultCheckList = checkList;
-    state.checkedList = checkList;
-    // 是否列展示全选
-    state.checkAll = checkList.length === columns.length;
-    inited = false;
-    handleVisibleChange();
-  }
-
-  // checkAll change
-  function onCheckAllChange(e: CheckboxChangeEvent) {
-    const checkList = plainSortOptions.value.map((item) => item.value);
-    plainSortOptions.value.forEach(
-      (item) => ((item as BasicColumn).defaultHidden = !e.target.checked),
-    );
-    if (e.target.checked) {
-      state.checkedList = checkList;
-      setColumns(checkList);
-    } else {
-      state.checkedList = [];
-      setColumns([]);
+      }
+      return true;
     }
-  }
-
-  const indeterminate = computed(() => {
-    const len = plainOptions.value.length;
-    let checkedLen = state.checkedList.length;
-    // unref(checkIndex) && checkedLen--;
-    return checkedLen > 0 && checkedLen < len;
-  });
-
-  // Trigger when check/uncheck a column
-  function onChange(checkedList: CheckboxValueType[]) {
-    const len = plainSortOptions.value.length;
-    state.checkAll = checkedList.length === len;
-    const sortList = unref(plainSortOptions).map((item) => item.value);
-    checkedList.sort((prev, next) => {
-      return sortList.indexOf(String(prev)) - sortList.indexOf(String(next));
-    });
-    unref(plainSortOptions).forEach((item) => {
-      (item as BasicColumn).defaultHidden = !checkedList.includes(item.value);
-    });
-    setColumns(checkedList as string[]);
-  }
+    return false;
+  };
 
+  // sortable 实例
   let sortable: Sortable;
+  // 排序
   let sortableOrder: string[] = [];
-  // reset columns
-  function reset() {
-    setColumns(cachePlainOptions.value);
-    init(true);
-    checkIndex.value = !!cacheTableProps.showIndexColumn;
-    checkSelect.value = !!cacheTableProps.rowSelection;
-    table.setProps({
-      showIndexColumn: checkIndex.value,
-      rowSelection: checkSelect.value ? defaultRowSelection : undefined,
-    });
-    sortable.sort(sortableOrder);
-  }
 
-  // Open the pop-up window for drag and drop initialization
-  function handleVisibleChange() {
-    if (inited) return;
-    nextTick(() => {
-      const columnListEl = unref(columnListRef);
-      if (!columnListEl) return;
-      const el = (columnListEl as any).$el;
-      if (!el) return;
-      // Drag and drop sort
+  // 获取数据列
+  const getTableColumns = () => {
+    return table
+      .getColumns({ ignoreIndex: true, ignoreAction: true })
+      .filter((col) => columnIfShow(col));
+  };
+
+  // 设置列表列
+  const tableColumnsSet = (columns: BasicColumn[]) => {
+    isInnerChange = true;
+    table?.setColumns(columns);
+
+    // 沿用逻辑
+    const columnChangeParams: ColumnChangeParam[] = columns.map((col) => ({
+      dataIndex: col.dataIndex ? col.dataIndex.toString() : '',
+      fixed: col.fixed,
+      visible: !col.defaultHidden,
+    }));
+    emit('columns-change', columnChangeParams);
+  };
+
+  // 列表列更新
+  const tableColumnsUpdate = () => {
+    // 考虑了所有列
+    const columns = cloneDeep(table.getColumns());
+
+    // 从左 fixed 最一列开始排序
+    let count = columns.filter((o) => o.fixed === 'left' || o.fixed === true).length;
+
+    // 按 columnOptions 的排序 调整 table.getColumns() 的顺序和值
+    for (const opt of columnOptions.value) {
+      const colIdx = columns.findIndex((o) => o.dataIndex === opt.value);
+      //
+      if (colIdx > -1) {
+        const target = columns[colIdx];
+        target.defaultHidden = opt.column?.defaultHidden;
+        target.fixed = opt.fixed;
+        columns.splice(colIdx, 1);
+        columns.splice(count++, 0, target); // 递增插入
+      }
+    }
+
+    // 设置列表列
+    tableColumnsSet(columns);
+  };
+
+  // 打开浮窗
+  const onOpenChange = async () => {
+    await nextTick();
+
+    if (columnOptionsRef.value) {
+      // 注册排序实例
+      const el = (columnOptionsRef.value as InstanceType<typeof Checkbox.Group>).$el;
       sortable = Sortablejs.create(unref(el), {
         animation: 500,
         delay: 400,
         delayOnTouchOnly: true,
         handle: '.table-column-drag-icon ',
+        dataIdAttr: 'data-no',
         onEnd: (evt) => {
           const { oldIndex, newIndex } = evt;
           if (isNil(oldIndex) || isNil(newIndex) || oldIndex === newIndex) {
             return;
           }
-          // Sort column
-          const columns = cloneDeep(plainSortOptions.value);
 
+          const options = cloneDeep(columnOptions.value);
+
+          // 排序
           if (oldIndex > newIndex) {
-            columns.splice(newIndex, 0, columns[oldIndex]);
-            columns.splice(oldIndex + 1, 1);
+            options.splice(newIndex, 0, options[oldIndex]);
+            options.splice(oldIndex + 1, 1);
           } else {
-            columns.splice(newIndex + 1, 0, columns[oldIndex]);
-            columns.splice(oldIndex, 1);
+            options.splice(newIndex + 1, 0, options[oldIndex]);
+            options.splice(oldIndex, 1);
           }
 
-          plainSortOptions.value = columns;
-          setColumns(columns.filter((item) => state.checkedList.includes(item.value)));
+          // 更新 列可选项
+          columnOptions.value = options;
+
+          // 列表列更新
+          tableColumnsUpdate();
+          // 更新列缓存
+          columnOptionsSave();
         },
       });
-      // 记录原始order 序列
-      sortableOrder = sortable.toArray();
-      inited = true;
-    });
-  }
 
-  // Control whether the serial number column is displayed
-  function handleIndexCheckChange(e: CheckboxChangeEvent) {
-    isSetPropsFromThis = true;
-    isSetColumnsFromThis = true;
+      // 从缓存恢复
+      sortable.sort(sortableOrder);
+    }
+  };
+
+  // remove消失的列、push新出现的列
+  const diff = () => {
+    if (typeof route.name === 'string') {
+      let cache = tableSettingStore.getColumns(route.name);
+      if (cache) {
+        // value、label是否一致
+        if (
+          JSON.stringify(columnOptions.value.map((o) => ({ value: o.value, label: o.label }))) !==
+          JSON.stringify(cache.map((o) => ({ value: o.value, label: o.label })))
+        ) {
+          const map = columnOptions.value.reduce((map, item) => {
+            map[item.value] = item.label;
+            return map;
+          }, {});
+          if (Array.isArray(cache)) {
+            // remove消失的列
+            cache = cache.filter((o) => map[o.value]);
+            // 更新label
+            cache.forEach((o) => {
+              o.label = map[o.value];
+            });
+            const cacheKeys = cache.map((o) => o.value);
+            // push新出现的列
+            cache = cache.concat(columnOptions.value.filter((o) => !cacheKeys.includes(o.value)));
+            // 更新缓存
+            tableSettingStore.setColumns(route.name, cache);
+          }
+        }
+      }
+    }
+  };
+
+  // 从缓存恢复
+  const restore = () => {
+    // 设置过才恢复
+    if (typeof tableSettingStore.getShowIndexColumn === 'boolean') {
+      isIndexColumnShow.value = tableSettingStore.getShowIndexColumn;
+    }
+    if (typeof tableSettingStore.getShowRowSelection === 'boolean') {
+      isRowSelectionShow.value = defaultIsRowSelectionShow && tableSettingStore.getShowRowSelection;
+    }
+
+    // 序号列更新
+    onIndexColumnShowChange({
+      target: { checked: isIndexColumnShow.value },
+    } as CheckboxChangeEvent);
+    // 选择列更新
+    onRowSelectionShowChange({
+      target: { checked: isRowSelectionShow.value },
+    } as CheckboxChangeEvent);
+
+    if (typeof route.name === 'string') {
+      const cache = tableSettingStore.getColumns(route.name);
+      // 设置过才恢复
+      if (Array.isArray(cache)) {
+        columnOptions.value = cache;
+      }
+    }
+  };
+
+  // 从 列可选项 更新 已选列
+  const columnCheckedOptionsUpdate = () => {
+    columnCheckedOptions.value = columnOptions.value
+      .filter((o) => !o.column?.defaultHidden)
+      .map((o) => o.value);
+  };
+  // 从 列可选项 更新 全选状态
+  const isColumnAllSelectedUpdate = () => {
+    isColumnAllSelected.value = columnOptions.value.length === columnCheckedOptions.value.length;
+  };
+  // 从 列可选项 更新 排序
+  const sortableOrderUpdateByOptions = (options: ColumnOptionsType[]) => {
+    sortableOrder = options.map((o) => o.value);
+  };
+  // 更新 showIndexColumn
+  const showIndexColumnUpdate = (showIndexColumn) => {
+    isInnerChange = true;
     table.setProps({
-      showIndexColumn: e.target.checked,
+      showIndexColumn,
     });
-  }
-
-  // Control whether the check box is displayed
-  function handleSelectCheckChange(e: CheckboxChangeEvent) {
-    isSetPropsFromThis = true;
-    isSetColumnsFromThis = true;
+  };
+  // 更新 rowSelection
+  const showRowSelectionUpdate = (showRowSelection) => {
+    isInnerChange = true;
     table.setProps({
-      rowSelection: e.target.checked ? defaultRowSelection : undefined,
+      rowSelection: showRowSelection
+        ? {
+            ...defaultRowSelection,
+            fixed: true,
+          }
+        : undefined,
     });
-  }
+  };
+
+  // 更新表单状态
+  const formUpdate = () => {
+    // 从 列可选项 更新 已选列
+    columnCheckedOptionsUpdate();
+
+    // 从 列可选项 更新 全选状态
+    isColumnAllSelectedUpdate();
+
+    // 从 列可选项 更新 排序
+    sortableOrderUpdateByOptions(columnOptions.value);
+
+    // 更新 showIndexColumn
+    showIndexColumnUpdate(isIndexColumnShow.value);
+
+    // 更新 showRowSelection
+    showRowSelectionUpdate(isRowSelectionShow.value);
+
+    // 列表列更新
+    tableColumnsUpdate();
+  };
+
+  // 默认值
+  let defaultIsIndexColumnShow: boolean = false;
+  let defaultIsRowSelectionShow: boolean = false;
+  let defaultRowSelection: TableRowSelection<Recordable<any>>;
+  let defaultColumnOptions: ColumnOptionsType[] = [];
+  let defaultSortableOrder: string[] = [];
+
+  const init = async () => {
+    if (!isRestored) {
+      await sortableFix();
+
+      // 获取数据列
+      const columns = getTableColumns();
+
+      // 沿用逻辑
+      table.setCacheColumns?.(columns);
+
+      // 生成 默认值
+      const options: ColumnOptionsType[] = [];
+      for (const col of columns) {
+        // 只缓存 string 类型的列
+        options.push({
+          label:
+            typeof col.title === 'string'
+              ? col.title
+              : col.customTitle === 'string'
+              ? col.customTitle
+              : '',
+          value:
+            typeof col.dataIndex === 'string'
+              ? col.dataIndex
+              : col.title === 'string'
+              ? col.title
+              : '',
+          column: {
+            defaultHidden: col.defaultHidden,
+          },
+          fixed: col.fixed,
+        });
+      }
 
-  function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
-    if (!state.checkedList.includes(item.dataIndex as string)) return;
+      // 默认值 缓存,浮窗出现的时候使用
+      defaultSortableOrder = options.map((o) => o.value);
 
-    const columns = getColumns().filter((c: BasicColumn) =>
-      state.checkedList.includes(c.dataIndex as string),
-    ) as BasicColumn[];
-    const isFixed = item.fixed === fixed ? false : fixed;
-    const index = columns.findIndex((col) => col.dataIndex === item.dataIndex);
-    if (index !== -1) {
-      columns[index].fixed = isFixed;
-    }
-    item.fixed = isFixed;
+      // 默认值 缓存
+      defaultIsIndexColumnShow = table.getBindValues.value.showIndexColumn || false;
+      defaultRowSelection = table.getRowSelection();
+      defaultIsRowSelectionShow = !!defaultRowSelection; // 设置了 rowSelection 才出现
+      defaultColumnOptions = options;
 
-    if (isFixed && !item.width) {
-      item.width = 100;
-    }
-    updateSortOption(item);
-    table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed });
-    setColumns(columns);
-  }
+      // 默认值 赋值
+      isIndexColumnShow.value = defaultIsIndexColumnShow;
+      isRowSelectionShow.value = defaultIsRowSelectionShow;
+      columnOptions.value = cloneDeep(options);
 
-  function setColumns(columns: BasicColumn[] | string[]) {
-    isSetPropsFromThis = true;
-    isSetColumnsFromThis = true;
-    table.setColumns(columns);
-    const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => {
-      const visible =
-        columns.findIndex(
-          (c: BasicColumn | string) =>
-            c === col.value || (typeof c !== 'string' && c.dataIndex === col.value),
-        ) !== -1;
-      return { dataIndex: col.value, fixed: col.fixed, visible };
-    });
+      // remove消失的列、push新出现的列
+      diff();
 
-    emit('columns-change', data);
-  }
+      // 从缓存恢复
+      restore();
 
-  function getPopupContainer() {
-    return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer();
-  }
+      // 更新表单状态
+      formUpdate();
+
+      isRestored = true;
+    }
+  };
 
-  function updateSortOption(column: BasicColumn) {
-    plainSortOptions.value.forEach((item) => {
-      if (item.value === column.dataIndex) {
-        Object.assign(item, column);
+  // 初始化
+  init();
+
+  // 外部列改变
+  const getColumns = computed(() => {
+    return table?.getColumns();
+  });
+  const getValues = computed(() => {
+    return table?.getBindValues;
+  });
+
+  onMounted(() => {
+    watch([getColumns, getValues], () => {
+      if (!isInnerChange) {
+        isRestored = false;
+        console.log('onMounted isRestored');
+        init();
+      } else {
+        isInnerChange = false;
       }
     });
-  }
+  });
 </script>
 <style lang="less">
   @prefix-cls: ~'@{namespace}-basic-column-setting';

+ 15 - 1
src/components/Table/src/components/settings/SizeSetting.vue

@@ -24,13 +24,17 @@
 </template>
 <script lang="ts" setup>
   import type { SizeType } from '../../types/table';
-  import { ref } from 'vue';
+  import { ref, onMounted } from 'vue';
   import { Tooltip, Dropdown, Menu, type MenuProps } from 'ant-design-vue';
   import { ColumnHeightOutlined } from '@ant-design/icons-vue';
   import { useI18n } from '@/hooks/web/useI18n';
   import { useTableContext } from '../../hooks/useTableContext';
   import { getPopupContainer } from '@/utils';
 
+  import { useTableSettingStore } from '@/store/modules/tableSetting';
+
+  const tableSettingStore = useTableSettingStore();
+
   defineOptions({ name: 'SizeSetting' });
 
   const table = useTableContext();
@@ -40,8 +44,18 @@
 
   const handleTitleClick: MenuProps['onClick'] = ({ key }) => {
     selectedKeysRef.value = [key as SizeType];
+
+    tableSettingStore.setTableSize(key as SizeType);
+
     table.setProps({
       size: key as SizeType,
     });
   };
+
+  onMounted(() => {
+    selectedKeysRef.value = [tableSettingStore.getTableSize];
+    table.setProps({
+      size: selectedKeysRef.value[0],
+    });
+  });
 </script>

+ 12 - 0
src/components/Table/src/types/table.ts

@@ -11,6 +11,7 @@ import type { ColumnProps } from 'ant-design-vue/lib/table';
 import { ComponentType } from './componentType';
 import { VueNode } from '@/utils/propTypes';
 import { RoleEnum } from '@/enums/roleEnum';
+import { FixedType } from 'ant-design-vue/es/vc-table/interface';
 
 export declare type SortOrder = 'ascend' | 'descend';
 
@@ -485,3 +486,14 @@ export type ColumnChangeParam = {
 export interface InnerHandlers {
   onColumnsChange: (data: ColumnChangeParam[]) => void;
 }
+
+export interface ColumnOptionsType {
+  value: string;
+  label: string;
+  //
+  column: {
+    defaultHidden?: boolean;
+  };
+  //
+  fixed?: FixedType;
+}

+ 3 - 0
src/enums/cacheEnum.ts

@@ -26,6 +26,9 @@ export const APP_LOCAL_CACHE_KEY = 'COMMON__LOCAL__KEY__';
 // base global session key
 export const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY__';
 
+// table 列设置
+export const TABLE_SETTING_KEY = 'TABLE__SETTING__KEY__';
+
 export enum CacheTypeEnum {
   SESSION,
   LOCAL,

+ 99 - 0
src/store/modules/tableSetting.ts

@@ -0,0 +1,99 @@
+import { defineStore } from 'pinia';
+
+import { TABLE_SETTING_KEY } from '@/enums/cacheEnum';
+
+import { Persistent } from '@/utils/cache/persistent';
+
+import type { TableSetting } from '#/store';
+import type { SizeType, ColumnOptionsType } from '@/components/Table/src/types/table';
+
+interface TableSettingState {
+  setting: Nullable<Partial<TableSetting>>;
+}
+
+export const useTableSettingStore = defineStore({
+  id: 'table-setting',
+  state: (): TableSettingState => ({
+    setting: Persistent.getLocal(TABLE_SETTING_KEY),
+  }),
+  getters: {
+    getTableSetting(state): Nullable<Partial<TableSetting>> {
+      return state.setting;
+    },
+    //
+    getTableSize(state) {
+      return state.setting?.size || 'middle';
+    },
+    //
+    getShowIndexColumn(state) {
+      return state.setting?.showIndexColumn;
+    },
+    //
+    getShowRowSelection(state) {
+      return state.setting?.showRowSelection;
+    },
+    //
+    getColumns(state) {
+      return (routerName: string) => {
+        return state.setting?.columns && state.setting?.columns[routerName]
+          ? state.setting?.columns[routerName]
+          : null;
+      };
+    },
+  },
+  actions: {
+    setTableSetting(setting: Partial<TableSetting>) {
+      this.setting = Object.assign({}, this.setting, setting);
+      Persistent.setLocal(TABLE_SETTING_KEY, this.setting, true);
+    },
+    resetTableSetting() {
+      Persistent.removeLocal(TABLE_SETTING_KEY, true);
+      this.setting = null;
+    },
+    //
+    setTableSize(size: SizeType) {
+      this.setTableSetting(
+        Object.assign({}, this.setting, {
+          size,
+        }),
+      );
+    },
+    //
+    setShowIndexColumn(show: boolean) {
+      this.setTableSetting(
+        Object.assign({}, this.setting, {
+          showIndexColumn: show,
+        }),
+      );
+    },
+    //
+    setShowRowSelection(show: boolean) {
+      this.setTableSetting(
+        Object.assign({}, this.setting, {
+          showRowSelection: show,
+        }),
+      );
+    },
+    //
+    setColumns(routerName: string, columns: Array<ColumnOptionsType>) {
+      this.setTableSetting(
+        Object.assign({}, this.setting, {
+          columns: {
+            ...this.setting?.columns,
+            [routerName]: columns,
+          },
+        }),
+      );
+    },
+    clearColumns(routerName: string) {
+      this.setTableSetting(
+        Object.assign({}, this.setting, {
+          columns: {
+            ...this.setting?.columns,
+            [routerName]: undefined,
+          },
+        }),
+      );
+    },
+  },
+});

+ 3 - 1
src/utils/cache/persistent.ts

@@ -1,4 +1,4 @@
-import type { LockInfo, UserInfo } from '#/store';
+import type { LockInfo, UserInfo, TableSetting } from '#/store';
 import type { ProjectConfig } from '#/config';
 import type { RouteLocationNormalized } from 'vue-router';
 
@@ -13,6 +13,7 @@ import {
   APP_LOCAL_CACHE_KEY,
   APP_SESSION_CACHE_KEY,
   MULTIPLE_TABS_KEY,
+  TABLE_SETTING_KEY,
 } from '@/enums/cacheEnum';
 import { DEFAULT_CACHE_TIME } from '@/settings/encryptionSetting';
 import { toRaw } from 'vue';
@@ -25,6 +26,7 @@ interface BasicStore {
   [LOCK_INFO_KEY]: LockInfo;
   [PROJ_CFG_KEY]: ProjectConfig;
   [MULTIPLE_TABS_KEY]: RouteLocationNormalized[];
+  [TABLE_SETTING_KEY]: Partial<TableSetting>;
 }
 
 type LocalStore = BasicStore;

+ 7 - 0
types/store.d.ts

@@ -51,3 +51,10 @@ export interface BeforeMiniState {
   menuMode?: MenuModeEnum;
   menuType?: MenuTypeEnum;
 }
+
+export interface TableSetting {
+  size: Nullable<SizeType>;
+  showIndexColumn: Nullable<boolean>;
+  columns: Recordable<Nullable<Array<ColumnOptionsType>>>;
+  showRowSelection: Nullable<boolean>;
+}