Skip to content

列控制与持久化 ​

示例展示了 ProTable 列控制配置(control.columnControl)与列显隐持久化(外部实现)。

本示例包含

  1. labels:为 label 为 vnode 的列提供下拉展示文本
  2. excludedKeys:列不出现在列控制下拉中(如操作列,始终展示)
  3. disabledKeys:列在下拉中显示但不可取消勾选(始终展示)
  4. disableCheckAll:禁用全选
  5. 持久化由外部实现:watch(table.columns, ...) 保存 + table.setColumn 恢复

表格展示 ​


姓名
No Data
Total 0
  • 1
Go to

代码实现 ​

vue
<script lang="ts" setup>
import { ElAlert, ElCard, ElTag } from 'element-plus';
import { ProTable, useTable } from '@qin-ui/element-plus-pro';
import { computed, h, ref, watch } from 'vue';

type Row = {
  name: string;
  age: number;
  gender: string;
  birthday: string;
  address: string;
};

// 模拟数据库接口:实际项目中替换为真实后端 API(按 userId 存储)
const mockApi = {
  async loadUserColumnState(): Promise<Record<string, { hidden?: boolean }>> {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(mockApi.state);
      }, 200);
    });
  },
  async saveUserColumnState(
    state: Record<string, { hidden?: boolean }>
  ): Promise<void> {
    return new Promise(resolve => {
      setTimeout(() => {
        mockApi.state = state;
        resolve();
      }, 200);
    });
  },
  state: { gender: { hidden: true } } as Record<string, { hidden?: boolean }>,
};

const table = useTable<Row>({
  searchFields: [{ label: '姓名', path: 'name', component: 'input' }],
  columns: [
    // label 为 vnode,通过 columnControl.labels 提供下拉展示文本
    {
      prop: 'name',
      label: '姓名',
      renderHeader: () => h(ElTag, { type: 'primary' }, () => '姓名'),
    },
    { prop: 'age', label: '年龄' },
    { prop: 'gender', label: '性别' },
    { prop: 'birthday', label: '生日' },
    // excludedKeys 固定列:不出现在列控制下拉中
    { prop: 'address', label: '家庭住址' },
  ],
});

const total = 888;
const mockListData = new Array(total).fill(null).map((_, index) => ({
  name: `张三${index}`,
  age: index,
  gender: index % 2 === 0 ? '男' : '女',
  birthday: '2023-01-01',
  address: '上海',
}));

const search = async () => {
  const { current, pageSize } = table.pageParam;
  return new Promise<void>(resolve => {
    setTimeout(() => {
      const list = mockListData.slice(
        (current - 1) * pageSize,
        current * pageSize
      );
      table.data.value = list;
      table.setPageParam({ total: total });
      resolve();
    }, 600);
  });
};

// 持久化(外部实现):监听 table.columns 的 hidden 变化保存到数据库
let restoring = false;
watch(
  () => table.columns.value.map(c => c.hidden),
  () => {
    if (restoring) return; // 恢复期间不重复保存
    const state: Record<string, { hidden?: boolean }> = {};
    table.columns.value.forEach(column => {
      const key = column.key ?? column.prop;
      if (key) state[String(key)] = { hidden: !!column.hidden };
    });
    mockApi.saveUserColumnState(state);
  },
  { deep: true }
);

// 恢复(外部实现):进入页面从数据库加载后回写列 hidden
const loaded = ref(false);
mockApi.loadUserColumnState().then(state => {
  restoring = true;
  Object.entries(state).forEach(([key, { hidden }]) => {
    table.setColumn(key as any, { hidden: !!hidden });
  });
  restoring = false;
  loaded.value = true;
});

const control = computed(() => ({
  columnControl: {
    excludedKeys: ['address'],
    disabledKeys: ['age'],
    disableCheckAll: false,
  },
}));
</script>

<template>
  <ElCard class="pro-table-demo">
    <ElAlert
      :title="loaded ? '已从数据库恢复列状态' : '正在加载列状态...'"
      type="info"
      show-icon
      style="margin-bottom: 16px"
    />
    <ProTable
      :table="table"
      :search="search"
      :control="control"
      class="pro-table"
    />
  </ElCard>
</template>

<style scoped lang="less">
.vp-doc .pro-table-demo {
  :deep(.pro-table) {
    table {
      display: table;
      margin: 0;
      overflow-x: initial;

      tr {
        background-color: initial;
        border-top: initial;
        transition: initial;
      }

      th,
      td {
        border: initial;
      }
    }
  }
}
</style>