Skip to content

Commit 3d63bb0

Browse files
committed
修复表格列设置错位及旧配置迁移
1 parent 6c894ff commit 3d63bb0

6 files changed

Lines changed: 296 additions & 108 deletions

File tree

.changeset/calm-tables-align.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@uozi-admin/curd": patch
3+
---
4+
5+
修复列设置后表头与表体错位,并兼容迁移已有用户的列顺序与显隐配置。

packages/curd/src/components/StdTable.vue

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { useRouteQuery } from '@vueuse/router'
77
import { Button, Popconfirm, Table } from 'antdv-next'
88
import { cloneDeep, debounce, get, isArray, isEqual, isNil } from 'lodash-es'
99
import { computed, h, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
10-
import { getRealContent } from '..'
10+
import { getColumnKey, getRealContent, normalizeTableColumnKeys } from '..'
1111
import { useLocale } from '../composables'
1212
import useCurdConfig from '../composables/useCurdConfig'
1313
import useDraggableTable from '../composables/useDraggableTable'
@@ -229,7 +229,11 @@ const dataColumns = computed<any>(() => {
229229
},
230230
})
231231
}
232-
return cols
232+
return normalizeTableColumnKeys(cols)
233+
})
234+
235+
const tableStructureKey = computed(() => {
236+
return JSON.stringify(dataColumns.value.map((column: StdTableColumn) => getColumnKey(column)))
233237
})
234238
235239
const searchColumns = computed(() => {
@@ -616,7 +620,6 @@ function SearchFormExtraRender() {
616620
</template>
617621
<TableColumnSettings
618622
:columns="tableColumns"
619-
:table-id="tableId"
620623
@change="onColumnSettingsChange"
621624
/>
622625
<Button
@@ -630,6 +633,7 @@ function SearchFormExtraRender() {
630633
<TableTopScrollbar />
631634
<Table
632635
:id="`std-table-${tableId}`"
636+
:key="tableStructureKey"
633637
v-model:pagination="pagination"
634638
:columns="dataColumns"
635639
:data-source="tableData"

packages/curd/src/components/TableColumnSettings.vue

Lines changed: 78 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
<script setup lang="ts">
22
import type { StdTableColumn } from '../types'
3+
import type { ManagedTableColumn, StoredColumnSetting } from '../utils'
34
import { HolderOutlined, SettingOutlined } from '@antdv-next/icons'
45
import { Button, Checkbox, Dropdown } from 'antdv-next'
56
import { cloneDeep } from 'lodash-es'
67
import Sortable from 'sortablejs'
7-
import { computed, h, nextTick, onMounted, ref, watch } from 'vue'
8+
import { computed, h, nextTick, onMounted, ref, shallowRef, toRaw, watch } from 'vue'
89
import { useLocale } from '../composables'
9-
import { getColumnKey, getRealContent } from '../utils'
10+
import {
11+
createManagedColumns,
12+
getRealContent,
13+
normalizeTableColumnKeys,
14+
parseColumnSettings,
15+
restoreColumnSettings,
16+
serializeColumnSettings,
17+
} from '../utils'
1018
1119
const props = defineProps<{
1220
columns: StdTableColumn[]
13-
tableId: string
1421
}>()
1522
1623
const emit = defineEmits<{
@@ -21,10 +28,11 @@ const { t } = useLocale()
2128
2229
const visible = ref(false)
2330
const sortableContainer = ref<HTMLElement>()
24-
const localColumns = ref<StdTableColumn[]>([])
31+
const localColumns = shallowRef<ManagedTableColumn[]>([])
2532
const slashRegex = /\//g
2633
// 临时配置(用于在确认前预览)
27-
const tempColumns = ref<StdTableColumn[]>([])
34+
const tempColumns = shallowRef<ManagedTableColumn[]>([])
35+
const orphanedSettings = shallowRef<StoredColumnSetting[]>([])
2836
2937
// 获取存储键 - 基于页面路径,确保配置持久化(去除查询参数)
3038
const storageKey = computed(() => {
@@ -34,68 +42,25 @@ const storageKey = computed(() => {
3442
3543
// 初始化列配置
3644
function initializeColumns() {
37-
const savedColumnKeys = getSavedConfig()
38-
const availableColumns = props.columns.filter(column => !isSystemColumn(column))
39-
40-
if (savedColumnKeys && savedColumnKeys.length > 0) {
41-
// 使用保存的配置:基于保存的key数组重新排序和筛选
42-
const columnsMap = new Map(availableColumns.map(col => [getColumnKey(col), col]))
43-
44-
// 创建已保存列的顺序和可见性映射
45-
const savedOrderMap = new Map<string, number>()
46-
const savedVisibilityMap = new Map<string, boolean>()
47-
savedColumnKeys.forEach((col, index) => {
48-
savedOrderMap.set(col.key, index)
49-
savedVisibilityMap.set(col.key, col.hiddenInTable)
50-
})
51-
52-
// 追踪已处理的保存列索引
53-
const processedSavedIndices = new Set<number>()
54-
const orderedColumns: StdTableColumn[] = []
45+
const availableColumns = createManagedColumns(
46+
props.columns.filter(column => !isSystemColumn(column)),
47+
)
48+
const savedConfig = getSavedConfig()
5549
56-
// 遍历原始列顺序,保持新列在原始位置
57-
availableColumns.forEach((column) => {
58-
const key = getColumnKey(column)
59-
const savedIndex = savedOrderMap.get(key as string)
60-
61-
if (savedIndex !== undefined) {
62-
// 这是一个已保存的列
63-
// 检查是否已经被处理过(可能在之前的循环中被插入了)
64-
if (processedSavedIndices.has(savedIndex)) {
65-
return // 跳过已处理的列
66-
}
67-
68-
// 先插入所有应该在它之前但还未处理的已保存列
69-
for (let i = 0; i < savedIndex; i++) {
70-
if (!processedSavedIndices.has(i)) {
71-
const savedKey = savedColumnKeys[i].key
72-
const savedColumn = columnsMap.get(savedKey)
73-
if (savedColumn) {
74-
savedColumn.hiddenInTable = savedColumnKeys[i].hiddenInTable
75-
orderedColumns.push(savedColumn)
76-
processedSavedIndices.add(i)
77-
}
78-
}
79-
}
80-
81-
// 插入当前列
82-
column.hiddenInTable = savedVisibilityMap.get(key as string) ?? false
83-
orderedColumns.push(column)
84-
processedSavedIndices.add(savedIndex)
85-
}
86-
else {
87-
// 这是一个新列,按原始顺序直接插入
88-
orderedColumns.push(column)
89-
}
90-
})
91-
92-
localColumns.value = orderedColumns
93-
tempColumns.value = cloneDeep(orderedColumns)
94-
}
95-
else {
50+
if (!savedConfig) {
9651
localColumns.value = availableColumns
97-
tempColumns.value = availableColumns
52+
tempColumns.value = cloneDeep(availableColumns)
53+
orphanedSettings.value = []
54+
return
9855
}
56+
57+
const restored = restoreColumnSettings(availableColumns, savedConfig.columns)
58+
localColumns.value = restored.columns
59+
tempColumns.value = cloneDeep(restored.columns)
60+
orphanedSettings.value = restored.orphaned
61+
62+
if (savedConfig.needsMigration)
63+
saveConfig()
9964
}
10065
10166
// 判断是否为系统列(如操作列、拖拽列等)
@@ -104,36 +69,29 @@ function isSystemColumn(column: StdTableColumn): boolean {
10469
return dataIndex === 'actions' || dataIndex === 'drag' || column.key === 'actions'
10570
}
10671
107-
// 获取保存的配置 - 现在返回字符串数组
108-
function getSavedConfig(): { key: string, hiddenInTable: boolean }[] | null {
109-
try {
110-
const saved = localStorage.getItem(storageKey.value)
111-
return saved ? JSON.parse(saved) : null
112-
}
113-
catch {
114-
return null
115-
}
72+
// 同时读取旧数组格式与当前版本配置
73+
function getSavedConfig() {
74+
return parseColumnSettings(localStorage.getItem(storageKey.value))
11675
}
11776
118-
// 保存配置 - 现在只保存显示列的key数组
77+
// 原位置升级配置,保留未出现在当前列定义中的历史设置
11978
function saveConfig() {
120-
const visibleColumnKeys = (localColumns.value as any[])
121-
.map(column => ({
122-
key: getColumnKey(column) as string,
123-
hiddenInTable: column.hiddenInTable,
124-
}))
125-
126-
localStorage.setItem(storageKey.value, JSON.stringify(visibleColumnKeys))
79+
const columns = toRaw(localColumns.value) as ManagedTableColumn[]
80+
const orphaned = toRaw(orphanedSettings.value) as StoredColumnSetting[]
81+
const config = serializeColumnSettings(columns, orphaned)
82+
localStorage.setItem(storageKey.value, JSON.stringify(config))
12783
}
12884
12985
// 生成最终的列配置
13086
function generateFinalColumns(): StdTableColumn[] {
131-
const visibleColumns = (localColumns.value as any[]).filter(column => !column.hiddenInTable)
87+
const visibleColumns = localColumns.value
88+
.filter(column => !column.column.hiddenInTable)
89+
.map(column => column.column)
13290
13391
// 添加系统列
13492
const systemColumns = props.columns.filter(column => isSystemColumn(column))
13593
136-
return [...visibleColumns, ...systemColumns]
94+
return normalizeTableColumnKeys([...visibleColumns, ...systemColumns])
13795
}
13896
13997
// 确认应用设置
@@ -152,32 +110,46 @@ function cancelSettings() {
152110
153111
// 全选/取消全选
154112
const checkAll = computed({
155-
get: (): boolean => (tempColumns.value as any[]).every(column => !column.hiddenInTable),
113+
get: (): boolean => tempColumns.value.every(column => !column.column.hiddenInTable),
156114
set: (value: boolean): void => {
157-
(tempColumns.value as any[]).forEach((column) => {
158-
column.hiddenInTable = !value // 修复:全选时应该显示所有列(hiddenInTable为false)
159-
})
115+
tempColumns.value = tempColumns.value.map(column => ({
116+
...column,
117+
column: {
118+
...column.column,
119+
hiddenInTable: !value,
120+
},
121+
}))
160122
},
161123
})
162124
163125
// 半选状态
164126
const indeterminate = computed(() => {
165-
const visibleCount = (tempColumns.value as any[]).filter(column => !column.hiddenInTable).length
127+
const visibleCount = tempColumns.value.filter(column => !column.column.hiddenInTable).length
166128
return visibleCount > 0 && visibleCount < tempColumns.value.length
167129
})
168130
131+
function updateColumnVisibility(id: string, checked: boolean) {
132+
tempColumns.value = tempColumns.value.map(column => column.id === id
133+
? {
134+
...column,
135+
column: {
136+
...column.column,
137+
hiddenInTable: !checked,
138+
},
139+
}
140+
: column)
141+
}
142+
169143
let sortableInstance: Sortable | null = null
170144
171145
// 重置列配置
172146
function resetColumns() {
173-
const defaultColumns = props.columns
174-
.filter(column => !isSystemColumn(column))
175-
.map((column) => {
176-
// 重置时所有列都默认显示
177-
const col = cloneDeep(column)
178-
col.hiddenInTable = false
179-
return col
180-
})
147+
const defaultColumns = createManagedColumns(
148+
props.columns.filter(column => !isSystemColumn(column)),
149+
)
150+
defaultColumns.forEach((column) => {
151+
column.column.hiddenInTable = false
152+
})
181153
tempColumns.value = defaultColumns
182154
}
183155
@@ -195,8 +167,10 @@ function initSortable() {
195167
const { oldIndex, newIndex } = evt
196168
if (oldIndex !== undefined && newIndex !== undefined && oldIndex !== newIndex) {
197169
// 更新临时列顺序
198-
const movedColumn = tempColumns.value.splice(oldIndex, 1)[0]
199-
tempColumns.value.splice(newIndex, 0, movedColumn)
170+
const reorderedColumns = [...tempColumns.value]
171+
const movedColumn = reorderedColumns.splice(oldIndex, 1)[0]
172+
reorderedColumns.splice(newIndex, 0, movedColumn)
173+
tempColumns.value = reorderedColumns
200174
}
201175
},
202176
})
@@ -255,19 +229,19 @@ watch(visible, async (newVisible) => {
255229
class="column-list"
256230
>
257231
<div
258-
v-for="column in tempColumns"
259-
:key="String(column.key || column.dataIndex)"
232+
v-for="managedColumn in tempColumns"
233+
:key="managedColumn.id"
260234
class="column-item"
261-
:data-key="String(column.key || column.dataIndex)"
235+
:data-key="managedColumn.id"
262236
>
263237
<div class="column-drag-handle">
264238
<HolderOutlined />
265239
</div>
266240
<Checkbox
267-
:checked="!column.hiddenInTable"
268-
@change="(e) => column.hiddenInTable = !e.target.checked"
241+
:checked="!managedColumn.column.hiddenInTable"
242+
@change="(e) => updateColumnVisibility(managedColumn.id, e.target.checked)"
269243
>
270-
<span class="column-title">{{ getRealContent(column.title) }}</span>
244+
<span class="column-title">{{ getRealContent(managedColumn.column.title) }}</span>
271245
</Checkbox>
272246
</div>
273247
</div>

0 commit comments

Comments
 (0)