This commit is contained in:
吴红兵
2026-03-07 01:34:48 +08:00
parent adc511cfdc
commit 94c3473958
1211 changed files with 599405 additions and 322105 deletions

View File

@@ -3,6 +3,7 @@
## 简介
`TableColumnControl` 是一个通用的表格列控制组件,支持:
- ✅ 列的显示/隐藏控制
- ✅ 列的拖拽排序
- ✅ 配置的自动保存和恢复(基于路由)
@@ -14,11 +15,11 @@
```vue
<script setup lang="ts">
import { ref } from 'vue'
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
import type { TableInstance } from 'element-plus'
import { ref } from 'vue';
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
import type { TableInstance } from 'element-plus';
const tableRef = ref<TableInstance>()
const tableRef = ref<TableInstance>();
</script>
```
@@ -26,41 +27,39 @@ const tableRef = ref<TableInstance>()
```vue
<template>
<div>
<!-- 列设置按钮放在表格上方 -->
<right-toolbar>
<TableColumnControl
:table-ref="tableRef"
trigger-circle
>
<template #trigger>
<el-tooltip content="列设置" placement="top">
<el-button circle style="margin-left: 0;">
<el-icon><Menu /></el-icon>
</el-button>
</el-tooltip>
</template>
</TableColumnControl>
</right-toolbar>
<!-- 表格 -->
<el-table ref="tableRef" :data="tableData">
<el-table-column prop="name" label="姓名" />
<el-table-column prop="age" label="年龄" />
<el-table-column prop="email" label="邮箱" />
<el-table-column label="操作" fixed="right">
<template #default="scope">
<el-button @click="handleEdit(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div>
<!-- 列设置按钮放在表格上方 -->
<right-toolbar>
<TableColumnControl :table-ref="tableRef" trigger-circle>
<template #trigger>
<el-tooltip content="列设置" placement="top">
<el-button circle style="margin-left: 0;">
<el-icon><Menu /></el-icon>
</el-button>
</el-tooltip>
</template>
</TableColumnControl>
</right-toolbar>
<!-- 表格 -->
<el-table ref="tableRef" :data="tableData">
<el-table-column prop="name" label="姓名" />
<el-table-column prop="age" label="年龄" />
<el-table-column prop="email" label="邮箱" />
<el-table-column label="操作" fixed="right">
<template #default="scope">
<el-button @click="handleEdit(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
```
### 步骤 3: 完成!
就这么简单!组件会自动:
- 从表格中提取列配置
- 根据当前路由自动生成存储 key
- 保存和恢复用户的列设置
@@ -71,149 +70,135 @@ const tableRef = ref<TableInstance>()
```vue
<template>
<div>
<right-toolbar>
<TableColumnControl
:columns="tableColumns"
v-model="visibleColumns"
trigger-circle
>
<template #trigger>
<el-tooltip content="列设置" placement="top">
<el-button circle>
<el-icon><Menu /></el-icon>
</el-button>
</el-tooltip>
</template>
</TableColumnControl>
</right-toolbar>
<el-table :data="tableData">
<el-table-column
v-for="col in sortedTableColumns"
v-if="checkColumnVisible(col.prop || '')"
:key="col.prop"
:prop="col.prop"
:label="col.label"
/>
</el-table>
</div>
<div>
<right-toolbar>
<TableColumnControl :columns="tableColumns" v-model="visibleColumns" trigger-circle>
<template #trigger>
<el-tooltip content="列设置" placement="top">
<el-button circle>
<el-icon><Menu /></el-icon>
</el-button>
</el-tooltip>
</template>
</TableColumnControl>
</right-toolbar>
<el-table :data="tableData">
<el-table-column
v-for="col in sortedTableColumns"
v-if="checkColumnVisible(col.prop || '')"
:key="col.prop"
:prop="col.prop"
:label="col.label"
/>
</el-table>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
import { ref, computed } from 'vue';
import { useRoute } from 'vue-router';
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
const route = useRoute()
const visibleColumns = ref<string[]>([])
const columnOrder = ref<string[]>([])
const route = useRoute();
const visibleColumns = ref<string[]>([]);
const columnOrder = ref<string[]>([]);
const tableColumns = [
{ prop: 'name', label: '姓名' },
{ prop: 'age', label: '年龄' },
{ prop: 'email', label: '邮箱' },
{ prop: 'action', label: '操作', fixed: true, alwaysShow: true }
]
{ prop: 'name', label: '姓名' },
{ prop: 'age', label: '年龄' },
{ prop: 'email', label: '邮箱' },
{ prop: 'action', label: '操作', fixed: true, alwaysShow: true },
];
// 检查列是否可见
const checkColumnVisible = (prop: string): boolean => {
if (visibleColumns.value.length === 0) return true
return visibleColumns.value.includes(prop)
}
if (visibleColumns.value.length === 0) return true;
return visibleColumns.value.includes(prop);
};
// 排序后的列
const sortedTableColumns = computed(() => {
if (columnOrder.value.length === 0) return tableColumns
const ordered: typeof tableColumns = []
const unordered: typeof tableColumns = []
columnOrder.value.forEach(key => {
const col = tableColumns.find(c => (c.prop || c.label) === key)
if (col) ordered.push(col)
})
tableColumns.forEach(col => {
const key = col.prop || col.label
if (!columnOrder.value.includes(key)) {
unordered.push(col)
}
})
return [...ordered, ...unordered]
})
if (columnOrder.value.length === 0) return tableColumns;
const ordered: typeof tableColumns = [];
const unordered: typeof tableColumns = [];
columnOrder.value.forEach((key) => {
const col = tableColumns.find((c) => (c.prop || c.label) === key);
if (col) ordered.push(col);
});
tableColumns.forEach((col) => {
const key = col.prop || col.label;
if (!columnOrder.value.includes(key)) {
unordered.push(col);
}
});
return [...ordered, ...unordered];
});
// 加载保存的配置
const loadSavedConfig = () => {
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
const storageKey = `table-columns-${routePath}`
const saved = localStorage.getItem(storageKey)
if (saved) {
try {
const savedColumns = JSON.parse(saved)
const validColumns = tableColumns
.filter(col => !col.alwaysShow && !col.fixed)
.map(col => col.prop || col.label)
const filteredSaved = savedColumns.filter((col: string) => validColumns.includes(col))
if (filteredSaved.length > 0) {
visibleColumns.value = filteredSaved
} else {
visibleColumns.value = validColumns
}
} catch (e) {
visibleColumns.value = tableColumns
.filter(col => !col.alwaysShow && !col.fixed)
.map(col => col.prop || col.label)
}
} else {
visibleColumns.value = tableColumns
.filter(col => !col.alwaysShow && !col.fixed)
.map(col => col.prop || col.label)
}
// 加载排序
const orderKey = `${storageKey}-order`
const savedOrder = localStorage.getItem(orderKey)
if (savedOrder) {
try {
columnOrder.value = JSON.parse(savedOrder)
} catch (e) {
columnOrder.value = tableColumns
.filter(col => !col.alwaysShow && !col.fixed)
.map(col => col.prop || col.label)
}
} else {
columnOrder.value = tableColumns
.filter(col => !col.alwaysShow && !col.fixed)
.map(col => col.prop || col.label)
}
}
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-');
const storageKey = `table-columns-${routePath}`;
const saved = localStorage.getItem(storageKey);
if (saved) {
try {
const savedColumns = JSON.parse(saved);
const validColumns = tableColumns.filter((col) => !col.alwaysShow && !col.fixed).map((col) => col.prop || col.label);
const filteredSaved = savedColumns.filter((col: string) => validColumns.includes(col));
if (filteredSaved.length > 0) {
visibleColumns.value = filteredSaved;
} else {
visibleColumns.value = validColumns;
}
} catch (e) {
visibleColumns.value = tableColumns.filter((col) => !col.alwaysShow && !col.fixed).map((col) => col.prop || col.label);
}
} else {
visibleColumns.value = tableColumns.filter((col) => !col.alwaysShow && !col.fixed).map((col) => col.prop || col.label);
}
// 加载排序
const orderKey = `${storageKey}-order`;
const savedOrder = localStorage.getItem(orderKey);
if (savedOrder) {
try {
columnOrder.value = JSON.parse(savedOrder);
} catch (e) {
columnOrder.value = tableColumns.filter((col) => !col.alwaysShow && !col.fixed).map((col) => col.prop || col.label);
}
} else {
columnOrder.value = tableColumns.filter((col) => !col.alwaysShow && !col.fixed).map((col) => col.prop || col.label);
}
};
// 监听列变化
const handleColumnChange = (columns: string[]) => {
visibleColumns.value = columns
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
const storageKey = `table-columns-${routePath}`
const selectableColumns = columns.filter(col => {
const column = tableColumns.find(c => (c.prop || c.label) === col)
return column && !column.alwaysShow && !column.fixed
})
localStorage.setItem(storageKey, JSON.stringify(selectableColumns))
}
visibleColumns.value = columns;
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-');
const storageKey = `table-columns-${routePath}`;
const selectableColumns = columns.filter((col) => {
const column = tableColumns.find((c) => (c.prop || c.label) === col);
return column && !column.alwaysShow && !column.fixed;
});
localStorage.setItem(storageKey, JSON.stringify(selectableColumns));
};
// 监听列排序变化
const handleColumnOrderChange = (order: string[]) => {
columnOrder.value = order
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
const storageKey = `table-columns-${routePath}-order`
localStorage.setItem(storageKey, JSON.stringify(order))
}
columnOrder.value = order;
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-');
const storageKey = `table-columns-${routePath}-order`;
localStorage.setItem(storageKey, JSON.stringify(order));
};
// 立即加载配置
loadSavedConfig()
loadSavedConfig();
</script>
```
@@ -223,30 +208,24 @@ loadSavedConfig()
```vue
<script setup lang="ts">
import { computed } from 'vue'
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
import { useTableColumnControl } from '/@/composables/useTableColumnControl'
import { computed } from 'vue';
import TableColumnControl from '/@/components/TableColumnControl/index.vue';
import { useTableColumnControl } from '/@/composables/useTableColumnControl';
const tableColumns = [
{ prop: 'name', label: '姓名' },
{ prop: 'age', label: '年龄' },
{ prop: 'email', label: '邮箱' },
{ prop: 'action', label: '操作', fixed: true, alwaysShow: true }
]
{ prop: 'name', label: '姓名' },
{ prop: 'age', label: '年龄' },
{ prop: 'email', label: '邮箱' },
{ prop: 'action', label: '操作', fixed: true, alwaysShow: true },
];
const {
visibleColumns,
columnOrder,
checkColumnVisible,
saveColumnConfig,
saveColumnOrder
} = useTableColumnControl({
columns: tableColumns
})
const { visibleColumns, columnOrder, checkColumnVisible, saveColumnConfig, saveColumnOrder } = useTableColumnControl({
columns: tableColumns,
});
const sortedTableColumns = computed(() => {
// ... 排序逻辑
})
// ... 排序逻辑
});
</script>
```
@@ -255,20 +234,17 @@ const sortedTableColumns = computed(() => {
### Q: 如何自定义存储 key
```vue
<TableColumnControl
:table-ref="tableRef"
storage-key="my-custom-key"
/>
<TableColumnControl :table-ref="tableRef" storage-key="my-custom-key" />
```
### Q: 如何设置始终显示的列?
```vue
<TableColumnControl
:table-ref="tableRef"
:auto-extract-options="{
alwaysShow: ['name', 'action']
}"
:table-ref="tableRef"
:auto-extract-options="{
alwaysShow: ['name', 'action'],
}"
/>
```
@@ -276,10 +252,10 @@ const sortedTableColumns = computed(() => {
```vue
<TableColumnControl
:table-ref="tableRef"
:auto-extract-options="{
defaultHidden: ['remark', 'description']
}"
:table-ref="tableRef"
:auto-extract-options="{
defaultHidden: ['remark', 'description'],
}"
/>
```
@@ -290,4 +266,3 @@ const sortedTableColumns = computed(() => {
## 完整示例
查看 `src/views/stuwork/classroomhygienemonthly/index.vue` 了解完整的使用示例。