xachary
2023-12-12 f4df2d5a4bd23f346af10580e5ab7de64933bb4c
feat: ColumnSetting and SizeSetting persist (#3398)

1个文件已添加
6个文件已修改
854 ■■■■ 已修改文件
src/components/Table/src/components/settings/ColumnSetting.vue 713 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Table/src/components/settings/SizeSetting.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Table/src/types/table.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/enums/cacheEnum.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/tableSetting.ts 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/cache/persistent.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
types/store.d.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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 route = useRoute();
  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 plainSortOptions = ref<Options[]>([]);
  const columnListRef = ref(null);
  const state = reactive<State>({
    checkAll: true,
    checkedList: [],
    defaultCheckList: [],
  });
  /** 缓存初始化props */
  let cacheTableProps: Partial<BasicTableProps<any>> = {};
  const checkIndex = ref(false);
  const checkSelect = ref(false);
  const { prefixCls } = useDesign('basic-column-setting');
  const getValues = computed(() => {
    return unref(table?.getBindValues) || {};
  });
  const attrs = useAttrs();
  const table = useTableContext();
  watchEffect(() => {
    const columns = table.getColumns();
    setTimeout(() => {
      if (isSetColumnsFromThis) {
        isSetColumnsFromThis = false;
      } else if (columns.length) {
        init();
      }
    }, 0);
  });
  const getPopupContainer = () => {
    return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer();
  };
  watchEffect(() => {
    const values = unref(getValues);
    if (isSetPropsFromThis) {
      isSetPropsFromThis = false;
    } else {
      cacheTableProps = cloneDeep(values);
  // 是否已经从缓存恢复
  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();
    }
    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;
  }
  // 全选
  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 = [];
    }
  };
  async function init(isReset = false) {
  // 半选状态
  const indeterminate = computed(() => {
    return (
      columnCheckedOptions.value.length > 0 &&
      columnCheckedOptions.value.length < columnOptions.value.length
    );
  });
  // 是否显示序号列
  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;
      }
    }
    // 列表列更新
    tableColumnsUpdate();
    // 更新列缓存
    columnOptionsSave();
  };
  // 沿用逻辑
  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;
    }
  }
    return false;
  };
  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[]);
  }
  // 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;
    table.setProps({
      showIndexColumn: e.target.checked,
    });
  }
  // Control whether the check box is displayed
  function handleSelectCheckChange(e: CheckboxChangeEvent) {
    isSetPropsFromThis = true;
    isSetColumnsFromThis = true;
    table.setProps({
      rowSelection: e.target.checked ? defaultRowSelection : undefined,
    });
  }
  function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
    if (!state.checkedList.includes(item.dataIndex as string)) return;
    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;
      // 从缓存恢复
      sortable.sort(sortableOrder);
    }
    item.fixed = isFixed;
  };
    if (isFixed && !item.width) {
      item.width = 100;
  // 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);
          }
        }
      }
    }
    updateSortOption(item);
    table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed });
    setColumns(columns);
  }
  };
  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 };
  // 从缓存恢复
  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,
    });
  };
  // 更新 rowSelection
  const showRowSelectionUpdate = (showRowSelection) => {
    isInnerChange = true;
    table.setProps({
      rowSelection: showRowSelection
        ? {
            ...defaultRowSelection,
            fixed: true,
          }
        : undefined,
    });
  };
    emit('columns-change', data);
  }
  // 更新表单状态
  const formUpdate = () => {
    // 从 列可选项 更新 已选列
    columnCheckedOptionsUpdate();
  function getPopupContainer() {
    return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer();
  }
    // 从 列可选项 更新 全选状态
    isColumnAllSelectedUpdate();
  function updateSortOption(column: BasicColumn) {
    plainSortOptions.value.forEach((item) => {
      if (item.value === column.dataIndex) {
        Object.assign(item, column);
    // 从 列可选项 更新 排序
    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,
        });
      }
      // 默认值 缓存,浮窗出现的时候使用
      defaultSortableOrder = options.map((o) => o.value);
      // 默认值 缓存
      defaultIsIndexColumnShow = table.getBindValues.value.showIndexColumn || false;
      defaultRowSelection = table.getRowSelection();
      defaultIsRowSelectionShow = !!defaultRowSelection; // 设置了 rowSelection 才出现
      defaultColumnOptions = options;
      // 默认值 赋值
      isIndexColumnShow.value = defaultIsIndexColumnShow;
      isRowSelectionShow.value = defaultIsRowSelectionShow;
      columnOptions.value = cloneDeep(options);
      // remove消失的列、push新出现的列
      diff();
      // 从缓存恢复
      restore();
      // 更新表单状态
      formUpdate();
      isRestored = true;
    }
  };
  // 初始化
  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';
src/components/Table/src/components/settings/SizeSetting.vue
@@ -24,12 +24,16 @@
</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' });
@@ -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>
src/components/Table/src/types/table.ts
@@ -11,6 +11,7 @@
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 interface InnerHandlers {
  onColumnsChange: (data: ColumnChangeParam[]) => void;
}
export interface ColumnOptionsType {
  value: string;
  label: string;
  //
  column: {
    defaultHidden?: boolean;
  };
  //
  fixed?: FixedType;
}
src/enums/cacheEnum.ts
@@ -26,6 +26,9 @@
// 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,
src/store/modules/tableSetting.ts
New file
@@ -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,
          },
        }),
      );
    },
  },
});
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 @@
  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 @@
  [LOCK_INFO_KEY]: LockInfo;
  [PROJ_CFG_KEY]: ProjectConfig;
  [MULTIPLE_TABS_KEY]: RouteLocationNormalized[];
  [TABLE_SETTING_KEY]: Partial<TableSetting>;
}
type LocalStore = BasicStore;
types/store.d.ts
@@ -51,3 +51,10 @@
  menuMode?: MenuModeEnum;
  menuType?: MenuTypeEnum;
}
export interface TableSetting {
  size: Nullable<SizeType>;
  showIndexColumn: Nullable<boolean>;
  columns: Recordable<Nullable<Array<ColumnOptionsType>>>;
  showRowSelection: Nullable<boolean>;
}