|
|
@@ -10,8 +10,10 @@ import {
|
|
|
provide,
|
|
|
ref,
|
|
|
useAttrs,
|
|
|
- useSlots
|
|
|
+ useSlots,
|
|
|
+ watch
|
|
|
} from 'vue'
|
|
|
+import { useRoute } from 'vue-router'
|
|
|
import { TableContextKey } from './token'
|
|
|
import type { ColumnAlign, ColumnFixed, ColumnSettingItem } from './token'
|
|
|
import type { DefaultRow } from 'element-plus/es/components/table/src/table/defaults'
|
|
|
@@ -38,15 +40,21 @@ interface Props
|
|
|
hoverHighlight?: boolean
|
|
|
align?: ColumnAlign
|
|
|
columnMaxWidth?: number
|
|
|
+ settingsCache?: boolean
|
|
|
+ settingsCacheKey?: string
|
|
|
}
|
|
|
|
|
|
const props = defineProps<Props>()
|
|
|
const attrs = useAttrs()
|
|
|
const slots = useSlots()
|
|
|
+const route = useRoute()
|
|
|
const tableRef = ref<TableInstance>()
|
|
|
const columnSettings = ref<ColumnSettingItem[]>([])
|
|
|
const columnDefaultSettings = ref<ColumnSettingItem[]>([])
|
|
|
const slotColumnSignature = ref('')
|
|
|
+const columnSettingsStorageKey = ref('')
|
|
|
+const isSyncingColumnSettings = ref(false)
|
|
|
+const columnSettingsStoragePrefix = 'zm-table:column-settings:'
|
|
|
|
|
|
const defaultOptions: Partial<Props> = {
|
|
|
size: 'default',
|
|
|
@@ -71,6 +79,8 @@ const bindProps = computed(() => {
|
|
|
hoverHighlight: _hoverHighlight,
|
|
|
align: _align,
|
|
|
columnMaxWidth,
|
|
|
+ settingsCache: _settingsCache,
|
|
|
+ settingsCacheKey: _settingsCacheKey,
|
|
|
...otherProps
|
|
|
} = props
|
|
|
|
|
|
@@ -93,6 +103,7 @@ const safeColumnMaxWidth = computed(() => {
|
|
|
const maxWidth = Number(props.columnMaxWidth)
|
|
|
return Number.isFinite(maxWidth) && maxWidth > 0 ? maxWidth : 360
|
|
|
})
|
|
|
+const isColumnSettingsCacheEnabled = computed(() => props.settingsCache !== false)
|
|
|
|
|
|
const forwardedSlots = computed(() => {
|
|
|
const { default: _default, ...restSlots } = slots
|
|
|
@@ -169,7 +180,7 @@ const getColumnMeta = (node: VNode, index: number, parentKey?: string): ColumnMe
|
|
|
label: getColumnLabel(node, index, parentKey),
|
|
|
visible: getPropValue(node.props, 'visible'),
|
|
|
action,
|
|
|
- configurable: !action && !hideInColumnSettings,
|
|
|
+ configurable: !hideInColumnSettings,
|
|
|
fixed: normalizeFixed(getPropValue(node.props, 'fixed')),
|
|
|
children: getColumnChildren(node)
|
|
|
.map((child, childIndex) => getColumnMeta(child, childIndex, key))
|
|
|
@@ -197,8 +208,11 @@ const syncColumnSettings = (nodes: VNode[]) => {
|
|
|
slotColumnSignature.value = signature
|
|
|
nextTick(() => {
|
|
|
const defaults = metas.map(createDefaultColumnSetting)
|
|
|
+ const storageKey = resolveColumnSettingsStorageKey(defaults)
|
|
|
+ const cachedSettings = loadColumnSettings(storageKey)
|
|
|
columnDefaultSettings.value = defaults
|
|
|
- columnSettings.value = mergeColumnSettings(defaults, columnSettings.value)
|
|
|
+ columnSettingsStorageKey.value = storageKey
|
|
|
+ setColumnSettings(mergeColumnSettings(defaults, cachedSettings || columnSettings.value))
|
|
|
})
|
|
|
}
|
|
|
|
|
|
@@ -291,9 +305,111 @@ const cloneColumnSettings = (items: ColumnSettingItem[]): ColumnSettingItem[] =>
|
|
|
}
|
|
|
|
|
|
const resetColumnSettings = () => {
|
|
|
- columnSettings.value = cloneColumnSettings(columnDefaultSettings.value)
|
|
|
+ setColumnSettings(cloneColumnSettings(columnDefaultSettings.value))
|
|
|
}
|
|
|
|
|
|
+const flattenColumnSettingKeys = (items: ColumnSettingItem[]): string[] => {
|
|
|
+ return items.flatMap((item) => [
|
|
|
+ item.key,
|
|
|
+ ...(item.children ? flattenColumnSettingKeys(item.children) : [])
|
|
|
+ ])
|
|
|
+}
|
|
|
+
|
|
|
+const encodeStorageKeyPart = (value: string) => {
|
|
|
+ try {
|
|
|
+ return encodeURIComponent(value)
|
|
|
+ } catch {
|
|
|
+ return value
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const resolveColumnSettingsStorageKey = (defaults: ColumnSettingItem[]) => {
|
|
|
+ if (!isColumnSettingsCacheEnabled.value) return ''
|
|
|
+
|
|
|
+ const userKey = props.settingsCacheKey?.trim()
|
|
|
+ if (userKey) return `${columnSettingsStoragePrefix}${encodeStorageKeyPart(userKey)}`
|
|
|
+
|
|
|
+ const routeKey = String(route.name || route.path || 'global')
|
|
|
+ const columnKey = flattenColumnSettingKeys(defaults).sort().join('|')
|
|
|
+ return `${columnSettingsStoragePrefix}${encodeStorageKeyPart(`${routeKey}:${columnKey}`)}`
|
|
|
+}
|
|
|
+
|
|
|
+const isColumnSettingItem = (item: unknown): item is ColumnSettingItem => {
|
|
|
+ if (!item || typeof item !== 'object') return false
|
|
|
+ const setting = item as ColumnSettingItem
|
|
|
+ const validFixed =
|
|
|
+ setting.fixed === false || setting.fixed === 'left' || setting.fixed === 'right'
|
|
|
+ return typeof setting.key === 'string' && typeof setting.visible === 'boolean' && validFixed
|
|
|
+}
|
|
|
+
|
|
|
+const normalizeCachedColumnSettings = (items: unknown): ColumnSettingItem[] => {
|
|
|
+ if (!Array.isArray(items)) return []
|
|
|
+
|
|
|
+ return items.filter(isColumnSettingItem).map((item) => ({
|
|
|
+ key: item.key,
|
|
|
+ label: typeof item.label === 'string' ? item.label : item.key,
|
|
|
+ visible: item.visible,
|
|
|
+ fixed: item.fixed,
|
|
|
+ children: item.children ? normalizeCachedColumnSettings(item.children) : undefined
|
|
|
+ }))
|
|
|
+}
|
|
|
+
|
|
|
+const loadColumnSettings = (storageKey: string) => {
|
|
|
+ if (!storageKey || typeof window === 'undefined') return null
|
|
|
+
|
|
|
+ try {
|
|
|
+ const rawValue = window.localStorage.getItem(storageKey)
|
|
|
+ if (!rawValue) return null
|
|
|
+
|
|
|
+ const cachedValue = JSON.parse(rawValue)
|
|
|
+ const settings = normalizeCachedColumnSettings(cachedValue?.settings)
|
|
|
+ return settings.length ? settings : null
|
|
|
+ } catch {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const saveColumnSettings = (storageKey: string, settings: ColumnSettingItem[]) => {
|
|
|
+ if (!storageKey || typeof window === 'undefined') return
|
|
|
+
|
|
|
+ try {
|
|
|
+ window.localStorage.setItem(
|
|
|
+ storageKey,
|
|
|
+ JSON.stringify({
|
|
|
+ version: 1,
|
|
|
+ settings
|
|
|
+ })
|
|
|
+ )
|
|
|
+ } catch {
|
|
|
+ // localStorage 可能被浏览器策略禁用,失败时不影响表格正常使用。
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const setColumnSettings = (settings: ColumnSettingItem[]) => {
|
|
|
+ isSyncingColumnSettings.value = true
|
|
|
+ columnSettings.value = settings
|
|
|
+ nextTick(() => {
|
|
|
+ isSyncingColumnSettings.value = false
|
|
|
+ saveColumnSettings(columnSettingsStorageKey.value, columnSettings.value)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+watch(
|
|
|
+ columnSettings,
|
|
|
+ (settings) => {
|
|
|
+ if (isSyncingColumnSettings.value) return
|
|
|
+ saveColumnSettings(columnSettingsStorageKey.value, settings)
|
|
|
+ },
|
|
|
+ { deep: true }
|
|
|
+)
|
|
|
+
|
|
|
+watch(
|
|
|
+ [() => props.settingsCache, () => props.settingsCacheKey, () => route.name, () => route.path],
|
|
|
+ () => {
|
|
|
+ slotColumnSignature.value = ''
|
|
|
+ }
|
|
|
+)
|
|
|
+
|
|
|
const applyColumnSetting = (node: VNode, setting: ColumnSettingItem) => {
|
|
|
const clonedNode = cloneVNode(
|
|
|
node,
|