|
|
@ -6,37 +6,38 @@ |
|
|
<Popover |
|
|
<Popover |
|
|
placement="bottomLeft" |
|
|
placement="bottomLeft" |
|
|
trigger="click" |
|
|
trigger="click" |
|
|
@visible-change="handleVisibleChange" |
|
|
@open-change="onOpenChange" |
|
|
:overlayClassName="`${prefixCls}__cloumn-list`" |
|
|
:overlayClassName="`${prefixCls}__column-list`" |
|
|
:getPopupContainer="getPopupContainer" |
|
|
:getPopupContainer="getPopupContainer" |
|
|
> |
|
|
> |
|
|
<template #title> |
|
|
<template #title> |
|
|
<div :class="`${prefixCls}__popover-title`"> |
|
|
<div :class="`${prefixCls}__popover-title`"> |
|
|
<Checkbox |
|
|
<Checkbox |
|
|
:indeterminate="indeterminate" |
|
|
:indeterminate="indeterminate" |
|
|
v-model:checked="state.checkAll" |
|
|
v-model:checked="isColumnAllSelected" |
|
|
@change="onCheckAllChange" |
|
|
@change="onColumnAllSelectChange" |
|
|
> |
|
|
> |
|
|
{{ t('component.table.settingColumnShow') }} |
|
|
{{ t('component.table.settingColumnShow') }} |
|
|
</Checkbox> |
|
|
</Checkbox> |
|
|
|
|
|
|
|
|
<Checkbox v-model:checked="checkIndex" @change="handleIndexCheckChange"> |
|
|
<Checkbox v-model:checked="isIndexColumnShow" @change="onIndexColumnShowChange"> |
|
|
{{ t('component.table.settingIndexColumnShow') }} |
|
|
{{ t('component.table.settingIndexColumnShow') }} |
|
|
</Checkbox> |
|
|
</Checkbox> |
|
|
|
|
|
<!-- 设置了 rowSelection 才出现 --> |
|
|
<Checkbox |
|
|
<Checkbox |
|
|
v-model:checked="checkSelect" |
|
|
v-model:checked="isRowSelectionShow" |
|
|
@change="handleSelectCheckChange" |
|
|
@change="onRowSelectionShowChange" |
|
|
:disabled="!defaultRowSelection" |
|
|
v-if="defaultIsRowSelectionShow" |
|
|
> |
|
|
> |
|
|
{{ t('component.table.settingSelectColumnShow') }} |
|
|
{{ t('component.table.settingSelectColumnShow') }} |
|
|
</Checkbox> |
|
|
</Checkbox> |
|
|
|
|
|
|
|
|
<Checkbox v-model:checked="checkDrag" @change="handleDragChange"> |
|
|
<Checkbox v-model:checked="isAllowResizeColumn" @change="onColumnAllowResizeChange"> |
|
|
{{ t('component.table.settingDragColumnShow') }} |
|
|
{{ t('component.table.settingDragColumnShow') }} |
|
|
</Checkbox> |
|
|
</Checkbox> |
|
|
|
|
|
|
|
|
<a-button size="small" type="link" @click="reset"> |
|
|
|
|
|
|
|
|
<a-button size="small" type="link" @click="onReset"> |
|
|
{{ t('common.resetText') }} |
|
|
{{ t('common.resetText') }} |
|
|
</a-button> |
|
|
</a-button> |
|
|
</div> |
|
|
</div> |
|
|
@ -44,12 +45,12 @@ |
|
|
|
|
|
|
|
|
<template #content> |
|
|
<template #content> |
|
|
<ScrollContainer> |
|
|
<ScrollContainer> |
|
|
<CheckboxGroup v-model:value="state.checkedList" @change="onChange" ref="columnListRef"> |
|
|
<Checkbox.Group v-model:value="columnCheckedOptions" ref="columnOptionsRef"> |
|
|
<template v-for="item in plainOptions" :key="item.value"> |
|
|
<template v-for="opt in columnOptions" :key="opt.value"> |
|
|
<div :class="`${prefixCls}__check-item`" v-if="!('ifShow' in item && !item.ifShow)"> |
|
|
<div :class="`${prefixCls}__check-item`" :data-no="opt.value"> |
|
|
<DragOutlined class="table-column-drag-icon" /> |
|
|
<DragOutlined class="table-column-drag-icon" /> |
|
|
<Checkbox :value="item.value"> |
|
|
<Checkbox :value="opt.value"> |
|
|
{{ item.label }} |
|
|
{{ opt.label }} |
|
|
</Checkbox> |
|
|
</Checkbox> |
|
|
|
|
|
|
|
|
<Tooltip |
|
|
<Tooltip |
|
|
@ -65,11 +66,11 @@ |
|
|
:class="[ |
|
|
:class="[ |
|
|
`${prefixCls}__fixed-left`, |
|
|
`${prefixCls}__fixed-left`, |
|
|
{ |
|
|
{ |
|
|
active: item.fixed === 'left', |
|
|
active: opt.fixed === 'left', |
|
|
disabled: !state.checkedList.includes(item.value), |
|
|
disabled: opt.value ? !columnCheckedOptions.includes(opt.value) : true, |
|
|
}, |
|
|
}, |
|
|
]" |
|
|
]" |
|
|
@click="handleColumnFixed(item, 'left')" |
|
|
@click="onColumnFixedChange(opt, 'left')" |
|
|
/> |
|
|
/> |
|
|
</Tooltip> |
|
|
</Tooltip> |
|
|
<Divider type="vertical" /> |
|
|
<Divider type="vertical" /> |
|
|
@ -86,16 +87,16 @@ |
|
|
:class="[ |
|
|
:class="[ |
|
|
`${prefixCls}__fixed-right`, |
|
|
`${prefixCls}__fixed-right`, |
|
|
{ |
|
|
{ |
|
|
active: item.fixed === 'right', |
|
|
active: opt.fixed === 'right', |
|
|
disabled: !state.checkedList.includes(item.value), |
|
|
disabled: opt.value ? !columnCheckedOptions.includes(opt.value) : true, |
|
|
}, |
|
|
}, |
|
|
]" |
|
|
]" |
|
|
@click="handleColumnFixed(item, 'right')" |
|
|
@click="onColumnFixedChange(opt, 'right')" |
|
|
/> |
|
|
/> |
|
|
</Tooltip> |
|
|
</Tooltip> |
|
|
</div> |
|
|
</div> |
|
|
</template> |
|
|
</template> |
|
|
</CheckboxGroup> |
|
|
</Checkbox.Group> |
|
|
</ScrollContainer> |
|
|
</ScrollContainer> |
|
|
</template> |
|
|
</template> |
|
|
<SettingOutlined /> |
|
|
<SettingOutlined /> |
|
|
@ -103,8 +104,8 @@ |
|
|
</Tooltip> |
|
|
</Tooltip> |
|
|
</template> |
|
|
</template> |
|
|
<script lang="ts" setup> |
|
|
<script lang="ts" setup> |
|
|
import type { BasicColumn, ColumnChangeParam } from '../../types/table'; |
|
|
import type { BasicColumn, ColumnOptionsType, ColumnChangeParam } from '../../types/table'; |
|
|
import { useAttrs, ref, reactive, watchEffect, nextTick, unref, computed } from 'vue'; |
|
|
import { ref, nextTick, unref, computed, useAttrs, watch, onMounted } from 'vue'; |
|
|
import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue'; |
|
|
import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue'; |
|
|
import type { CheckboxChangeEvent } 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 { SettingOutlined, DragOutlined } from '@ant-design/icons-vue'; |
|
|
@ -113,269 +114,526 @@ |
|
|
import { useI18n } from '/@/hooks/web/useI18n'; |
|
|
import { useI18n } from '/@/hooks/web/useI18n'; |
|
|
import { useTableContext } from '../../hooks/useTableContext'; |
|
|
import { useTableContext } from '../../hooks/useTableContext'; |
|
|
import { useDesign } from '/@/hooks/web/useDesign'; |
|
|
import { useDesign } from '/@/hooks/web/useDesign'; |
|
|
// import { useSortable } from '/@/hooks/web/useSortable'; |
|
|
import { isFunction, isNil, isNumber } from '/@/utils/is'; |
|
|
import { isFunction, isNullAndUnDef, isNumber } from '/@/utils/is'; |
|
|
|
|
|
import { getPopupContainer as getParentContainer } from '/@/utils'; |
|
|
import { getPopupContainer as getParentContainer } from '/@/utils'; |
|
|
import { cloneDeep, omit } from 'lodash-es'; |
|
|
import { cloneDeep, omit } from 'lodash-es'; |
|
|
import Sortablejs from 'sortablejs'; |
|
|
import Sortablejs from 'sortablejs'; |
|
|
import type Sortable from 'sortablejs'; |
|
|
import { INDEX_COLUMN_FLAG } from '/@/components/Table/src/const'; |
|
|
|
|
|
|
|
|
interface State { |
|
|
|
|
|
checkAll: boolean; |
|
|
|
|
|
isInit?: boolean; |
|
|
|
|
|
checkedList: string[]; |
|
|
|
|
|
defaultCheckList: string[]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
interface Options { |
|
|
|
|
|
label: string; |
|
|
|
|
|
value: string; |
|
|
|
|
|
fixed?: boolean | 'left' | 'right'; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const CheckboxGroup = Checkbox.Group; |
|
|
|
|
|
const emits = defineEmits(['columns-change']); |
|
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n(); |
|
|
|
|
|
const attrs = useAttrs(); |
|
|
|
|
|
const table = useTableContext(); |
|
|
|
|
|
|
|
|
|
|
|
const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys'); |
|
|
// 列表设置缓存 |
|
|
let inited = false; |
|
|
import { useTableSettingStore } from '/@/store/modules/tableSetting'; |
|
|
|
|
|
import { useRoute } from 'vue-router'; |
|
|
|
|
|
import { TableRowSelection } from '/@/components/Table/src/types/table'; |
|
|
|
|
|
|
|
|
const cachePlainOptions = ref<Options[]>([]); |
|
|
const tableSettingStore = useTableSettingStore(); |
|
|
const plainOptions = ref<Options[] | any>([]); |
|
|
|
|
|
|
|
|
|
|
|
const plainSortOptions = ref<Options[]>([]); |
|
|
// defineOptions({ name: 'ColumnSetting' }); |
|
|
|
|
|
const emit = defineEmits(['columns-change']); |
|
|
|
|
|
|
|
|
const columnListRef = ref<ComponentRef>(null); |
|
|
const route = useRoute(); |
|
|
|
|
|
|
|
|
const state = reactive<State>({ |
|
|
|
|
|
checkAll: true, |
|
|
|
|
|
checkedList: [], |
|
|
|
|
|
defaultCheckList: [], |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const checkIndex = ref(false); |
|
|
|
|
|
const checkSelect = ref(false); |
|
|
|
|
|
const checkDrag = ref(false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n(); |
|
|
const { prefixCls } = useDesign('basic-column-setting'); |
|
|
const { prefixCls } = useDesign('basic-column-setting'); |
|
|
|
|
|
|
|
|
const getValues = computed(() => { |
|
|
const attrs = useAttrs(); |
|
|
return unref(table?.getBindValues) || {}; |
|
|
const table = useTableContext(); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
watchEffect(() => { |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
const columns = table.getColumns(); |
|
|
|
|
|
if (columns.length && !state.isInit) { |
|
|
|
|
|
init(); |
|
|
|
|
|
} |
|
|
|
|
|
}, 10); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
watchEffect(() => { |
|
|
const props = withDefaults( |
|
|
const values = unref(getValues); |
|
|
defineProps<{ |
|
|
checkIndex.value = !!values.showIndexColumn; |
|
|
/** |
|
|
checkSelect.value = !!values.rowSelection; |
|
|
* 是否缓存列的设置 |
|
|
}); |
|
|
*/ |
|
|
|
|
|
cache?: boolean; |
|
|
|
|
|
}>(), |
|
|
|
|
|
{ |
|
|
|
|
|
cache: () => false, |
|
|
|
|
|
}, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
function getColumns() { |
|
|
const getPopupContainer = () => { |
|
|
const ret: Options[] = []; |
|
|
return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer(); |
|
|
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, |
|
|
let isRestored = false; |
|
|
...item, |
|
|
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; |
|
|
}); |
|
|
}); |
|
|
return ret; |
|
|
// 从 列可选项 更新 全选状态 |
|
|
} |
|
|
isColumnAllSelectedUpdate(); |
|
|
|
|
|
|
|
|
function init() { |
|
|
|
|
|
const columns = getColumns(); |
|
|
|
|
|
|
|
|
|
|
|
const checkList = table |
|
|
// 列表列更新 |
|
|
.getColumns({ ignoreAction: true, ignoreIndex: true }) |
|
|
tableColumnsUpdate(); |
|
|
.map((item) => { |
|
|
// 更新列缓存 |
|
|
if (item.defaultHidden) { |
|
|
props.cache && columnOptionsSave(); |
|
|
return ''; |
|
|
|
|
|
} |
|
|
|
|
|
return item.dataIndex || item.title; |
|
|
|
|
|
}) |
|
|
|
|
|
.filter(Boolean) as string[]; |
|
|
|
|
|
|
|
|
|
|
|
if (!plainOptions.value.length) { |
|
|
|
|
|
plainOptions.value = columns; |
|
|
|
|
|
plainSortOptions.value = columns; |
|
|
|
|
|
cachePlainOptions.value = columns; |
|
|
|
|
|
state.defaultCheckList = checkList; |
|
|
|
|
|
} else { |
|
|
|
|
|
// const fixedColumns = columns.filter((item) => |
|
|
|
|
|
// Reflect.has(item, 'fixed') |
|
|
|
|
|
// ) as BasicColumn[]; |
|
|
|
|
|
|
|
|
|
|
|
unref(plainOptions).forEach((item: BasicColumn) => { |
|
|
|
|
|
const findItem = columns.find((col: BasicColumn) => col.dataIndex === item.dataIndex); |
|
|
|
|
|
if (findItem) { |
|
|
|
|
|
item.fixed = findItem.fixed; |
|
|
|
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
state.isInit = true; |
|
|
|
|
|
state.checkedList = checkList; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// checkAll change |
|
|
// 全选 |
|
|
function onCheckAllChange(e: CheckboxChangeEvent) { |
|
|
const isColumnAllSelected = ref<boolean>(false); |
|
|
const checkList = plainOptions.value.map((item) => item.value); |
|
|
const onColumnAllSelectChange = () => { |
|
|
if (e.target.checked) { |
|
|
if (columnCheckedOptions.value.length < columnOptions.value.length) { |
|
|
state.checkedList = checkList; |
|
|
columnCheckedOptions.value = columnOptions.value.map((o) => o.value); |
|
|
setColumns(checkList); |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
state.checkedList = []; |
|
|
columnCheckedOptions.value = []; |
|
|
setColumns([]); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 半选状态 |
|
|
const indeterminate = computed(() => { |
|
|
const indeterminate = computed(() => { |
|
|
const len = plainOptions.value.length; |
|
|
return ( |
|
|
let checkedLen = state.checkedList.length; |
|
|
columnCheckedOptions.value.length > 0 && |
|
|
unref(checkIndex) && checkedLen--; |
|
|
columnCheckedOptions.value.length < columnOptions.value.length |
|
|
return checkedLen > 0 && checkedLen < len; |
|
|
); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Trigger when check/uncheck a column |
|
|
// 是否显示序号列 |
|
|
function onChange(checkedList: string[]) { |
|
|
const isIndexColumnShow = ref<boolean>(false); |
|
|
const len = plainSortOptions.value.length; |
|
|
// 序号列更新 |
|
|
state.checkAll = checkedList.length === len; |
|
|
const onIndexColumnShowChange = (e: CheckboxChangeEvent) => { |
|
|
const sortList = unref(plainSortOptions).map((item) => item.value); |
|
|
// 更新 showIndexColumn |
|
|
checkedList.sort((prev, next) => { |
|
|
showIndexColumnUpdate(e.target.checked); |
|
|
return sortList.indexOf(prev) - sortList.indexOf(next); |
|
|
// 更新 showIndexColumn 缓存 |
|
|
|
|
|
props.cache && |
|
|
|
|
|
typeof route.name === 'string' && |
|
|
|
|
|
tableSettingStore.setShowIndexColumn(route.name, e.target.checked); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 是否显示选择列 |
|
|
|
|
|
const isRowSelectionShow = ref<boolean>(false); |
|
|
|
|
|
// 选择列更新 |
|
|
|
|
|
const onRowSelectionShowChange = (e: CheckboxChangeEvent) => { |
|
|
|
|
|
// 更新 showRowSelection |
|
|
|
|
|
showRowSelectionUpdate(e.target.checked); |
|
|
|
|
|
// 更新 showRowSelection 缓存 |
|
|
|
|
|
props.cache && |
|
|
|
|
|
typeof route.name === 'string' && |
|
|
|
|
|
tableSettingStore.setShowRowSelection(route.name, e.target.checked); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 是否可改变列大小 |
|
|
|
|
|
const isAllowResizeColumn = ref<boolean>(false); |
|
|
|
|
|
// 改变列大小 |
|
|
|
|
|
const onColumnAllowResizeChange = (e: CheckboxChangeEvent) => { |
|
|
|
|
|
const columns = getTableColumns(); |
|
|
|
|
|
columns.forEach((col) => { |
|
|
|
|
|
if (isNumber(col.width)) { |
|
|
|
|
|
col.resizable = e.target.checked; |
|
|
|
|
|
} |
|
|
}); |
|
|
}); |
|
|
setColumns(checkedList); |
|
|
props.cache && |
|
|
} |
|
|
typeof route.name === 'string' && |
|
|
|
|
|
tableSettingStore.setAllowResizeColumn(route.name, e.target.checked); |
|
|
let sortable: Sortable; |
|
|
} |
|
|
let sortableOrder: string[] = []; |
|
|
|
|
|
// reset columns |
|
|
// 更新列缓存 |
|
|
function reset() { |
|
|
const columnOptionsSave = () => { |
|
|
state.checkedList = [...state.defaultCheckList]; |
|
|
if (typeof route.name === 'string') { |
|
|
state.checkAll = true; |
|
|
// 按路由 name 作为缓存的key(若一个路由内存在多个表格,需自行调整缓存key来源) |
|
|
plainOptions.value = unref(cachePlainOptions); |
|
|
tableSettingStore.setColumns(route.name, columnOptions.value); |
|
|
plainSortOptions.value = unref(cachePlainOptions); |
|
|
} |
|
|
setColumns(table.getCacheColumns()); |
|
|
}; |
|
|
sortable.sort(sortableOrder); |
|
|
|
|
|
} |
|
|
// 重置 |
|
|
|
|
|
const onReset = () => { |
|
|
// Open the pop-up window for drag and drop initialization |
|
|
// 重置默认值 |
|
|
function handleVisibleChange() { |
|
|
isIndexColumnShow.value = defaultIsIndexColumnShow; |
|
|
if (inited) return; |
|
|
// 序号列更新 |
|
|
nextTick(() => { |
|
|
onIndexColumnShowChange({ |
|
|
const columnListEl = unref(columnListRef); |
|
|
target: { checked: defaultIsIndexColumnShow }, |
|
|
if (!columnListEl) return; |
|
|
} as CheckboxChangeEvent); |
|
|
const el = columnListEl.$el as any; |
|
|
// 重置默认值 |
|
|
if (!el) return; |
|
|
isRowSelectionShow.value = defaultIsRowSelectionShow; |
|
|
// Drag and drop sort |
|
|
// 选择列更新 |
|
|
sortable = Sortablejs.create(unref(el), { |
|
|
onRowSelectionShowChange({ |
|
|
|
|
|
target: { checked: defaultIsRowSelectionShow }, |
|
|
|
|
|
} as CheckboxChangeEvent); |
|
|
|
|
|
// 重置默认值 |
|
|
|
|
|
columnOptions.value = cloneDeep(defaultColumnOptions); |
|
|
|
|
|
// 更新表单状态 |
|
|
|
|
|
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(); |
|
|
|
|
|
// 更新列缓存 |
|
|
|
|
|
props.cache && columnOptionsSave(); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 沿用逻辑 |
|
|
|
|
|
const sortableFix = async () => { |
|
|
|
|
|
// Sortablejs存在bug,不知道在哪个步骤中会向el append了一个childNode,因此这里先清空childNode |
|
|
|
|
|
// 有可能复现上述问题的操作:拖拽一个元素,快速的上下移动,最后放到最后的位置中松手 |
|
|
|
|
|
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 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 true; |
|
|
|
|
|
} |
|
|
|
|
|
return false; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 获取数据列 |
|
|
|
|
|
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.flag !== INDEX_COLUMN_FLAG && (o.fixed === 'left' || o.fixed === true), |
|
|
|
|
|
).length; |
|
|
|
|
|
|
|
|
|
|
|
// 序号列提前 |
|
|
|
|
|
if (isIndexColumnShow.value) { |
|
|
|
|
|
count++; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 按 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); // 递增插入 |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 是否存在 action |
|
|
|
|
|
const actionIndex = columns.findIndex((o) => o.dataIndex === 'action'); |
|
|
|
|
|
if (actionIndex > -1) { |
|
|
|
|
|
const actionCol = columns.splice(actionIndex, 1); |
|
|
|
|
|
columns.push(actionCol[0]); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 设置列表列 |
|
|
|
|
|
tableColumnsSet(columns); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 打开浮窗 |
|
|
|
|
|
const onOpenChange = async () => { |
|
|
|
|
|
await nextTick(); |
|
|
|
|
|
|
|
|
|
|
|
if (columnOptionsRef.value) { |
|
|
|
|
|
// 注册排序实例 |
|
|
|
|
|
const el = (columnOptionsRef.value as InstanceType<typeof Checkbox.Group>).$el; |
|
|
|
|
|
Sortablejs.create(unref(el), { |
|
|
animation: 500, |
|
|
animation: 500, |
|
|
delay: 400, |
|
|
delay: 400, |
|
|
delayOnTouchOnly: true, |
|
|
delayOnTouchOnly: true, |
|
|
handle: '.table-column-drag-icon ', |
|
|
handle: '.table-column-drag-icon ', |
|
|
|
|
|
dataIdAttr: 'data-no', |
|
|
onEnd: (evt) => { |
|
|
onEnd: (evt) => { |
|
|
const { oldIndex, newIndex } = evt; |
|
|
const { oldIndex, newIndex } = evt; |
|
|
if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) { |
|
|
if (isNil(oldIndex) || isNil(newIndex) || oldIndex === newIndex) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
// Sort column |
|
|
|
|
|
const columns = cloneDeep(plainSortOptions.value); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const options = cloneDeep(columnOptions.value); |
|
|
|
|
|
|
|
|
|
|
|
// 排序 |
|
|
if (oldIndex > newIndex) { |
|
|
if (oldIndex > newIndex) { |
|
|
columns.splice(newIndex, 0, columns[oldIndex]); |
|
|
options.splice(newIndex, 0, options[oldIndex]); |
|
|
columns.splice(oldIndex + 1, 1); |
|
|
options.splice(oldIndex + 1, 1); |
|
|
} else { |
|
|
} else { |
|
|
columns.splice(newIndex + 1, 0, columns[oldIndex]); |
|
|
options.splice(newIndex + 1, 0, options[oldIndex]); |
|
|
columns.splice(oldIndex, 1); |
|
|
options.splice(oldIndex, 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
plainSortOptions.value = columns; |
|
|
// 更新 列可选项 |
|
|
// fix: 修复ColumnSetting中默认隐藏列拖拽排序错误的bug (#1931) |
|
|
columnOptions.value = options; |
|
|
// https://github.com/vbenjs/vue-vben-admin/commit/50468e9581c93e95df21447bec30b6148541c46b |
|
|
|
|
|
setColumns( |
|
|
// 列表列更新 |
|
|
columns |
|
|
tableColumnsUpdate(); |
|
|
.map((col: Options) => col.value) |
|
|
// 更新列缓存 |
|
|
.filter((value: string) => state.checkedList.includes(value)), |
|
|
props.cache && columnOptionsSave(); |
|
|
); |
|
|
|
|
|
}, |
|
|
}, |
|
|
}); |
|
|
}); |
|
|
// 记录原始order 序列 |
|
|
} |
|
|
sortableOrder = sortable.toArray(); |
|
|
}; |
|
|
inited = true; |
|
|
|
|
|
|
|
|
// 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); |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
// Control whether the serial number column is displayed |
|
|
// 从缓存恢复 |
|
|
function handleIndexCheckChange(e: CheckboxChangeEvent) { |
|
|
const restore = () => { |
|
|
table.setProps({ |
|
|
if (typeof route.name === 'string') { |
|
|
showIndexColumn: e.target.checked, |
|
|
const isIndexColumnShowCache = tableSettingStore.getShowIndexColumn(route.name); |
|
|
}); |
|
|
// 设置过才恢复 |
|
|
|
|
|
if (typeof isIndexColumnShowCache === 'boolean') { |
|
|
|
|
|
isIndexColumnShow.value = defaultIsIndexColumnShow && isIndexColumnShowCache; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Control whether the check box is displayed |
|
|
const isRowSelectionShowCache = tableSettingStore.getShowRowSelection(route.name); |
|
|
function handleSelectCheckChange(e: CheckboxChangeEvent) { |
|
|
// 设置过才恢复 |
|
|
table.setProps({ |
|
|
if (typeof isRowSelectionShowCache === 'boolean') { |
|
|
rowSelection: e.target.checked ? defaultRowSelection : undefined, |
|
|
isRowSelectionShow.value = defaultIsRowSelectionShow && isRowSelectionShowCache; |
|
|
}); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function handleDragChange(e: CheckboxChangeEvent) { |
|
|
const allowResizeColumnCache = tableSettingStore.getAllowResizeColumn(route.name); |
|
|
const columns = getColumns() as BasicColumn[]; |
|
|
// 设置过才恢复 |
|
|
columns.forEach((col) => { |
|
|
if (typeof allowResizeColumnCache === 'boolean') { |
|
|
if (isNumber(col.width)) { |
|
|
isAllowResizeColumn.value = defaultIsAllowResizeColumn && allowResizeColumnCache; |
|
|
col.resizable = e.target.checked; |
|
|
|
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
setColumns(columns); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
// 序号列更新 |
|
|
|
|
|
onIndexColumnShowChange({ |
|
|
|
|
|
target: { checked: isIndexColumnShow.value }, |
|
|
|
|
|
} as CheckboxChangeEvent); |
|
|
|
|
|
// 选择列更新 |
|
|
|
|
|
onRowSelectionShowChange({ |
|
|
|
|
|
target: { checked: isRowSelectionShow.value }, |
|
|
|
|
|
} as CheckboxChangeEvent); |
|
|
|
|
|
|
|
|
function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') { |
|
|
if (typeof route.name === 'string') { |
|
|
if (!state.checkedList.includes(item.dataIndex as string)) return; |
|
|
const cache = tableSettingStore.getColumns(route.name); |
|
|
|
|
|
// 设置过才恢复 |
|
|
const columns = getColumns().filter((c: BasicColumn) => |
|
|
if (Array.isArray(cache)) { |
|
|
state.checkedList.includes(c.dataIndex as string), |
|
|
columnOptions.value = cache; |
|
|
) 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; |
|
|
|
|
|
} |
|
|
} |
|
|
item.fixed = isFixed; |
|
|
}; |
|
|
|
|
|
|
|
|
if (isFixed && !item.width) { |
|
|
// 从 列可选项 更新 已选列 |
|
|
item.width = 100; |
|
|
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; |
|
|
|
|
|
}; |
|
|
|
|
|
// 更新 showIndexColumn |
|
|
|
|
|
const showIndexColumnUpdate = (showIndexColumn) => { |
|
|
|
|
|
isInnerChange = true; |
|
|
|
|
|
table.setProps({ |
|
|
|
|
|
showIndexColumn, |
|
|
|
|
|
}); |
|
|
|
|
|
}; |
|
|
|
|
|
// 更新 rowSelection |
|
|
|
|
|
const showRowSelectionUpdate = (showRowSelection) => { |
|
|
|
|
|
isInnerChange = true; |
|
|
|
|
|
table.setProps({ |
|
|
|
|
|
rowSelection: showRowSelection |
|
|
|
|
|
? { |
|
|
|
|
|
...omit(defaultRowSelection, ['selectedRowKeys']), |
|
|
|
|
|
fixed: true, |
|
|
} |
|
|
} |
|
|
table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed }); |
|
|
: undefined, |
|
|
setColumns(columns); |
|
|
}); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 更新表单状态 |
|
|
|
|
|
const formUpdate = () => { |
|
|
|
|
|
// 从 列可选项 更新 已选列 |
|
|
|
|
|
columnCheckedOptionsUpdate(); |
|
|
|
|
|
|
|
|
|
|
|
// 从 列可选项 更新 全选状态 |
|
|
|
|
|
isColumnAllSelectedUpdate(); |
|
|
|
|
|
|
|
|
|
|
|
// 更新 showIndexColumn |
|
|
|
|
|
showIndexColumnUpdate(isIndexColumnShow.value); |
|
|
|
|
|
|
|
|
|
|
|
// 更新 showRowSelection |
|
|
|
|
|
showRowSelectionUpdate(isRowSelectionShow.value); |
|
|
|
|
|
|
|
|
|
|
|
// 列表列更新 |
|
|
|
|
|
tableColumnsUpdate(); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 默认值 |
|
|
|
|
|
let defaultIsIndexColumnShow: boolean = false; |
|
|
|
|
|
let defaultIsRowSelectionShow: boolean = false; |
|
|
|
|
|
let defaultIsAllowResizeColumn: boolean = false; |
|
|
|
|
|
let defaultRowSelection: TableRowSelection<Recordable<any>>; |
|
|
|
|
|
let defaultColumnOptions: ColumnOptionsType[] = []; |
|
|
|
|
|
|
|
|
|
|
|
const init = async () => { |
|
|
|
|
|
if (!isRestored) { |
|
|
|
|
|
// 获取数据列 |
|
|
|
|
|
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, |
|
|
|
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function setColumns(columns: BasicColumn[] | string[]) { |
|
|
// 默认值 缓存 |
|
|
table.setColumns(columns); |
|
|
defaultIsIndexColumnShow = table.getBindValues.value.showIndexColumn || false; |
|
|
const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => { |
|
|
defaultRowSelection = table.getRowSelection(); |
|
|
const visible = |
|
|
defaultIsRowSelectionShow = !!defaultRowSelection; // 设置了 rowSelection 才出现 |
|
|
columns.findIndex( |
|
|
defaultColumnOptions = options; |
|
|
(c: BasicColumn | string) => |
|
|
|
|
|
c === col.value || (typeof c !== 'string' && c.dataIndex === col.value), |
|
|
// 默认值 赋值 |
|
|
) !== -1; |
|
|
isIndexColumnShow.value = defaultIsIndexColumnShow; |
|
|
return { dataIndex: col.value, fixed: col.fixed, visible }; |
|
|
isRowSelectionShow.value = defaultIsRowSelectionShow; |
|
|
}); |
|
|
columnOptions.value = cloneDeep(options); |
|
|
|
|
|
|
|
|
|
|
|
// remove消失的列、push新出现的列 |
|
|
|
|
|
props.cache && diff(); |
|
|
|
|
|
|
|
|
emits('columns-change', data); |
|
|
// 从缓存恢复 |
|
|
|
|
|
props.cache && restore(); |
|
|
|
|
|
|
|
|
|
|
|
// 更新表单状态 |
|
|
|
|
|
formUpdate(); |
|
|
|
|
|
|
|
|
|
|
|
isRestored = true; |
|
|
} |
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
function getPopupContainer() { |
|
|
// 初始化 |
|
|
return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer(); |
|
|
const once = async () => { |
|
|
|
|
|
// 仅执行一次 |
|
|
|
|
|
await sortableFix(); |
|
|
|
|
|
init(); |
|
|
|
|
|
}; |
|
|
|
|
|
once(); |
|
|
|
|
|
|
|
|
|
|
|
// 外部列改变 |
|
|
|
|
|
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> |
|
|
</script> |
|
|
<style lang="less"> |
|
|
<style lang="less"> |
|
|
@prefix-cls: ~'@{namespace}-basic-column-setting'; |
|
|
@prefix-cls: ~'@{namespace}-basic-column-setting'; |
|
|
@ -387,8 +645,8 @@ |
|
|
|
|
|
|
|
|
.@{prefix-cls} { |
|
|
.@{prefix-cls} { |
|
|
&__popover-title { |
|
|
&__popover-title { |
|
|
position: relative; |
|
|
|
|
|
display: flex; |
|
|
display: flex; |
|
|
|
|
|
position: relative; |
|
|
align-items: center; |
|
|
align-items: center; |
|
|
justify-content: space-between; |
|
|
justify-content: space-between; |
|
|
} |
|
|
} |
|
|
@ -428,7 +686,7 @@ |
|
|
transform: rotate(180deg); |
|
|
transform: rotate(180deg); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
&__cloumn-list { |
|
|
&__column-list { |
|
|
svg { |
|
|
svg { |
|
|
width: 1em !important; |
|
|
width: 1em !important; |
|
|
height: 1em !important; |
|
|
height: 1em !important; |
|
|
@ -442,6 +700,7 @@ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.ant-checkbox-group { |
|
|
.ant-checkbox-group { |
|
|
|
|
|
display: inline-block; |
|
|
width: 100%; |
|
|
width: 100%; |
|
|
min-width: 260px; |
|
|
min-width: 260px; |
|
|
// flex-wrap: wrap; |
|
|
// flex-wrap: wrap; |
|
|
|