| | |
| | | <Popover |
| | | placement="bottomLeft" |
| | | trigger="click" |
| | | @visible-change="handleVisibleChange" |
| | | :overlayClassName="`${prefixCls}__cloumn-list`" |
| | | @open-change="onOpenChange" |
| | | :overlayClassName="`${prefixCls}__column-list`" |
| | | :getPopupContainer="getPopupContainer" |
| | | > |
| | | <template #title> |
| | | <div :class="`${prefixCls}__popover-title`"> |
| | | <Checkbox |
| | | :indeterminate="indeterminate" |
| | | v-model:checked="checkAll" |
| | | @change="onCheckAllChange" |
| | | v-model:checked="isColumnAllSelected" |
| | | @change="onColumnAllSelectChange" |
| | | > |
| | | {{ t('component.table.settingColumnShow') }} |
| | | </Checkbox> |
| | | |
| | | <Checkbox v-model:checked="checkIndex" @change="handleIndexCheckChange"> |
| | | <Checkbox v-model:checked="isIndexColumnShow" @change="onIndexColumnShowChange"> |
| | | {{ t('component.table.settingIndexColumnShow') }} |
| | | </Checkbox> |
| | | |
| | | <!-- 设置了 rowSelection 才出现 --> |
| | | <Checkbox |
| | | v-model:checked="checkSelect" |
| | | @change="handleSelectCheckChange" |
| | | :disabled="!defaultRowSelection" |
| | | v-model:checked="isRowSelectionShow" |
| | | @change="onRowSelectionShowChange" |
| | | v-if="defaultIsRowSelectionShow" |
| | | > |
| | | {{ t('component.table.settingSelectColumnShow') }} |
| | | </Checkbox> |
| | | |
| | | <a-button size="small" type="link" @click="reset"> |
| | | <a-button size="small" type="link" @click="onReset"> |
| | | {{ t('common.resetText') }} |
| | | </a-button> |
| | | </div> |
| | |
| | | |
| | | <template #content> |
| | | <ScrollContainer> |
| | | <CheckboxGroup v-model:value="checkedList" @change="onChange" ref="columnListRef"> |
| | | <template v-for="item in plainOptions" :key="item.value"> |
| | | <div :class="`${prefixCls}__check-item`" v-if="!('ifShow' in item && !item.ifShow)"> |
| | | <Checkbox.Group v-model:value="columnCheckedOptions" ref="columnOptionsRef"> |
| | | <template v-for="opt in columnOptions" :key="opt.value"> |
| | | <div :class="`${prefixCls}__check-item`" :data-no="opt.value"> |
| | | <DragOutlined class="table-column-drag-icon" /> |
| | | <Checkbox :value="item.value"> |
| | | {{ item.label }} |
| | | <Checkbox :value="opt.value"> |
| | | {{ opt.label }} |
| | | </Checkbox> |
| | | |
| | | <Tooltip |
| | |
| | | :class="[ |
| | | `${prefixCls}__fixed-left`, |
| | | { |
| | | active: item.fixed === 'left', |
| | | disabled: !checkedList.includes(item.value), |
| | | active: opt.fixed === 'left', |
| | | disabled: opt.value ? !columnCheckedOptions.includes(opt.value) : true, |
| | | }, |
| | | ]" |
| | | @click="handleColumnFixed(item, 'left')" |
| | | @click="onColumnFixedChange(opt, 'left')" |
| | | /> |
| | | </Tooltip> |
| | | <Divider type="vertical" /> |
| | |
| | | :class="[ |
| | | `${prefixCls}__fixed-right`, |
| | | { |
| | | active: item.fixed === 'right', |
| | | disabled: !checkedList.includes(item.value), |
| | | active: opt.fixed === 'right', |
| | | disabled: opt.value ? !columnCheckedOptions.includes(opt.value) : true, |
| | | }, |
| | | ]" |
| | | @click="handleColumnFixed(item, 'right')" |
| | | @click="onColumnFixedChange(opt, 'right')" |
| | | /> |
| | | </Tooltip> |
| | | </div> |
| | | </template> |
| | | </CheckboxGroup> |
| | | </Checkbox.Group> |
| | | </ScrollContainer> |
| | | </template> |
| | | <SettingOutlined /> |
| | | </Popover> |
| | | </Tooltip> |
| | | </template> |
| | | <script lang="ts"> |
| | | import type { BasicColumn, BasicTableProps, ColumnChangeParam } from '../../types/table'; |
| | | import { |
| | | defineComponent, |
| | | ref, |
| | | reactive, |
| | | toRefs, |
| | | watchEffect, |
| | | nextTick, |
| | | unref, |
| | | computed, |
| | | } from 'vue'; |
| | | <script lang="ts" setup> |
| | | import type { BasicColumn, ColumnOptionsType, ColumnChangeParam } from '../../types/table'; |
| | | import { ref, nextTick, unref, computed, useAttrs, watch, onMounted } from 'vue'; |
| | | import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue'; |
| | | import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface'; |
| | | import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue'; |
| | | import Icon from '@/components/Icon/Icon.vue'; |
| | | import { ScrollContainer } from '/@/components/Container'; |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | import { ScrollContainer } from '@/components/Container'; |
| | | import { useI18n } from '@/hooks/web/useI18n'; |
| | | import { useTableContext } from '../../hooks/useTableContext'; |
| | | import { useDesign } from '/@/hooks/web/useDesign'; |
| | | // import { useSortable } from '/@/hooks/web/useSortable'; |
| | | import { isFunction, isNullAndUnDef } from '/@/utils/is'; |
| | | import { getPopupContainer as getParentContainer } from '/@/utils'; |
| | | import { useDesign } from '@/hooks/web/useDesign'; |
| | | import { isFunction, isNil } from '@/utils/is'; |
| | | import { getPopupContainer as getParentContainer } from '@/utils'; |
| | | import { cloneDeep, omit } from 'lodash-es'; |
| | | 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[]; |
| | | } |
| | | // 列表设置缓存 |
| | | import { useTableSettingStore } from '@/store/modules/tableSetting'; |
| | | import { useRoute } from 'vue-router'; |
| | | import { TableRowSelection } from '@/components/Table/src/types/table'; |
| | | |
| | | interface Options { |
| | | label: string; |
| | | value: string; |
| | | fixed?: boolean | 'left' | 'right'; |
| | | } |
| | | const tableSettingStore = useTableSettingStore(); |
| | | |
| | | export default defineComponent({ |
| | | name: 'ColumnSetting', |
| | | components: { |
| | | SettingOutlined, |
| | | Popover, |
| | | Tooltip, |
| | | Checkbox, |
| | | CheckboxGroup: Checkbox.Group, |
| | | DragOutlined, |
| | | ScrollContainer, |
| | | Divider, |
| | | Icon, |
| | | defineOptions({ name: 'ColumnSetting' }); |
| | | const emit = defineEmits(['columns-change']); |
| | | |
| | | const route = useRoute(); |
| | | |
| | | const { t } = useI18n(); |
| | | const { prefixCls } = useDesign('basic-column-setting'); |
| | | |
| | | const attrs = useAttrs(); |
| | | const table = useTableContext(); |
| | | |
| | | const props = withDefaults( |
| | | defineProps<{ |
| | | /** |
| | | * 是否缓存列的设置 |
| | | */ |
| | | cache?: boolean; |
| | | }>(), |
| | | { |
| | | cache: () => false, |
| | | }, |
| | | emits: ['columns-change'], |
| | | ); |
| | | |
| | | setup(_, { emit, attrs }) { |
| | | const { t } = useI18n(); |
| | | const table = useTableContext(); |
| | | const getPopupContainer = () => { |
| | | return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer(); |
| | | }; |
| | | |
| | | const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys'); |
| | | let inited = false; |
| | | // 是否当前的setColums触发的 |
| | | let isSetColumnsFromThis = false; |
| | | // 是否当前组件触发的setProps |
| | | let isSetPropsFromThis = false; |
| | | // 默认值 |
| | | let defaultIsIndexColumnShow: boolean = false; |
| | | let defaultIsRowSelectionShow: boolean = false; |
| | | let defaultRowSelection: TableRowSelection<Recordable<any>>; |
| | | let defaultColumnOptions: ColumnOptionsType[] = []; |
| | | |
| | | const cachePlainOptions = ref<Options[]>([]); |
| | | const plainOptions = ref<Options[] | any>([]); |
| | | // 是否已经从缓存恢复 |
| | | let isRestored = false; |
| | | let isInnerChange = false; |
| | | |
| | | const plainSortOptions = ref<Options[]>([]); |
| | | // 列可选项 |
| | | 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; |
| | | }); |
| | | // 从 列可选项 更新 全选状态 |
| | | isColumnAllSelectedUpdate(); |
| | | |
| | | const columnListRef = ref(null); |
| | | // 列表列更新 |
| | | tableColumnsUpdate(); |
| | | // 更新列缓存 |
| | | props.cache && columnOptionsSave(); |
| | | } |
| | | }); |
| | | |
| | | const state = reactive<State>({ |
| | | checkAll: true, |
| | | checkedList: [], |
| | | defaultCheckList: [], |
| | | }); |
| | | /** 缓存初始化props */ |
| | | let cacheTableProps: Partial<BasicTableProps<any>> = {}; |
| | | const checkIndex = ref(false); |
| | | const checkSelect = ref(false); |
| | | // 全选 |
| | | const isColumnAllSelected = ref<boolean>(false); |
| | | const onColumnAllSelectChange = () => { |
| | | if (columnCheckedOptions.value.length < columnOptions.value.length) { |
| | | columnCheckedOptions.value = columnOptions.value.map((o) => o.value); |
| | | } else { |
| | | columnCheckedOptions.value = []; |
| | | } |
| | | }; |
| | | |
| | | const { prefixCls } = useDesign('basic-column-setting'); |
| | | // 半选状态 |
| | | const indeterminate = computed(() => { |
| | | return ( |
| | | columnCheckedOptions.value.length > 0 && |
| | | columnCheckedOptions.value.length < columnOptions.value.length |
| | | ); |
| | | }); |
| | | |
| | | const getValues = computed(() => { |
| | | return unref(table?.getBindValues) || {}; |
| | | }); |
| | | // 是否显示序号列 |
| | | const isIndexColumnShow = ref<boolean>(false); |
| | | // 序号列更新 |
| | | const onIndexColumnShowChange = (e: CheckboxChangeEvent) => { |
| | | // 更新 showIndexColumn |
| | | showIndexColumnUpdate(e.target.checked); |
| | | // 更新 showIndexColumn 缓存 |
| | | props.cache && |
| | | typeof route.name === 'string' && |
| | | tableSettingStore.setShowIndexColumn(route.name, e.target.checked); |
| | | }; |
| | | |
| | | watchEffect(() => { |
| | | const columns = table.getColumns(); |
| | | setTimeout(() => { |
| | | if (isSetColumnsFromThis) { |
| | | isSetColumnsFromThis = false; |
| | | } else if (columns.length) { |
| | | init(); |
| | | // 是否显示选择列 |
| | | 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 columnOptionsSave = () => { |
| | | if (typeof route.name === 'string') { |
| | | // 按路由 name 作为缓存的key(若一个路由内存在多个表格,需自行调整缓存key来源) |
| | | tableSettingStore.setColumns(route.name, columnOptions.value); |
| | | } |
| | | }; |
| | | |
| | | // 重置 |
| | | const onReset = () => { |
| | | // 重置默认值 |
| | | isIndexColumnShow.value = defaultIsIndexColumnShow; |
| | | // 序号列更新 |
| | | onIndexColumnShowChange({ |
| | | target: { checked: defaultIsIndexColumnShow }, |
| | | } as CheckboxChangeEvent); |
| | | // 重置默认值 |
| | | isRowSelectionShow.value = defaultIsRowSelectionShow; |
| | | // 选择列更新 |
| | | 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, |
| | | delay: 400, |
| | | delayOnTouchOnly: true, |
| | | handle: '.table-column-drag-icon ', |
| | | dataIdAttr: 'data-no', |
| | | onEnd: (evt) => { |
| | | const { oldIndex, newIndex } = evt; |
| | | if (isNil(oldIndex) || isNil(newIndex) || oldIndex === newIndex) { |
| | | return; |
| | | } |
| | | }, 0); |
| | | }); |
| | | |
| | | watchEffect(() => { |
| | | const values = unref(getValues); |
| | | if (isSetPropsFromThis) { |
| | | isSetPropsFromThis = false; |
| | | } else { |
| | | cacheTableProps = cloneDeep(values); |
| | | } |
| | | checkIndex.value = !!values.showIndexColumn; |
| | | checkSelect.value = !!values.rowSelection; |
| | | }); |
| | | const options = cloneDeep(columnOptions.value); |
| | | |
| | | function getColumns() { |
| | | const ret: Options[] = []; |
| | | 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, |
| | | ...item, |
| | | }); |
| | | }); |
| | | return ret; |
| | | } |
| | | |
| | | async function init(isReset = false) { |
| | | // Sortablejs存在bug,不知道在哪个步骤中会向el append了一个childNode,因此这里先清空childNode |
| | | // 有可能复现上述问题的操作:拖拽一个元素,快速的上下移动,最后放到最后的位置中松手 |
| | | plainOptions.value = []; |
| | | const columnListEl = unref(columnListRef); |
| | | if (columnListEl && (columnListEl as any).$el) { |
| | | const el = (columnListEl as any).$el as Element; |
| | | Array.from(el.children).forEach((item) => el.removeChild(item)); |
| | | } |
| | | await nextTick(); |
| | | const columns = isReset ? cloneDeep(cachePlainOptions.value) : getColumns(); |
| | | |
| | | const checkList = table |
| | | .getColumns({ ignoreAction: true, ignoreIndex: true }) |
| | | .map((item) => { |
| | | if (item.defaultHidden) { |
| | | return ''; |
| | | } |
| | | return item.dataIndex || item.title; |
| | | }) |
| | | .filter(Boolean) as string[]; |
| | | plainOptions.value = columns; |
| | | plainSortOptions.value = columns; |
| | | // 更新缓存配置 |
| | | table.setCacheColumns?.(columns); |
| | | !isReset && (cachePlainOptions.value = cloneDeep(columns)); |
| | | state.defaultCheckList = checkList; |
| | | state.checkedList = checkList; |
| | | // 是否列展示全选 |
| | | state.checkAll = checkList.length === columns.length; |
| | | inited = false; |
| | | handleVisibleChange(); |
| | | } |
| | | |
| | | // checkAll change |
| | | function onCheckAllChange(e: CheckboxChangeEvent) { |
| | | const checkList = plainSortOptions.value.map((item) => item.value); |
| | | plainSortOptions.value.forEach( |
| | | (item) => ((item as BasicColumn).defaultHidden = !e.target.checked), |
| | | ); |
| | | if (e.target.checked) { |
| | | state.checkedList = checkList; |
| | | setColumns(checkList); |
| | | } else { |
| | | state.checkedList = []; |
| | | setColumns([]); |
| | | } |
| | | } |
| | | |
| | | const indeterminate = computed(() => { |
| | | const len = plainOptions.value.length; |
| | | let checkedLen = state.checkedList.length; |
| | | // unref(checkIndex) && checkedLen--; |
| | | return checkedLen > 0 && checkedLen < len; |
| | | }); |
| | | |
| | | // Trigger when check/uncheck a column |
| | | function onChange(checkedList: string[]) { |
| | | const len = plainSortOptions.value.length; |
| | | state.checkAll = checkedList.length === len; |
| | | const sortList = unref(plainSortOptions).map((item) => item.value); |
| | | checkedList.sort((prev, next) => { |
| | | return sortList.indexOf(prev) - sortList.indexOf(next); |
| | | }); |
| | | unref(plainSortOptions).forEach((item) => { |
| | | (item as BasicColumn).defaultHidden = !checkedList.includes(item.value); |
| | | }); |
| | | setColumns(checkedList); |
| | | } |
| | | |
| | | let sortable: Sortable; |
| | | let sortableOrder: string[] = []; |
| | | // reset columns |
| | | function reset() { |
| | | setColumns(cachePlainOptions.value); |
| | | init(true); |
| | | checkIndex.value = !!cacheTableProps.showIndexColumn; |
| | | checkSelect.value = !!cacheTableProps.rowSelection; |
| | | table.setProps({ |
| | | showIndexColumn: checkIndex.value, |
| | | rowSelection: checkSelect.value ? defaultRowSelection : undefined, |
| | | }); |
| | | sortable.sort(sortableOrder); |
| | | } |
| | | |
| | | // Open the pop-up window for drag and drop initialization |
| | | function handleVisibleChange() { |
| | | if (inited) return; |
| | | nextTick(() => { |
| | | const columnListEl = unref(columnListRef); |
| | | if (!columnListEl) return; |
| | | const el = (columnListEl as any).$el; |
| | | if (!el) return; |
| | | // Drag and drop sort |
| | | sortable = Sortablejs.create(unref(el), { |
| | | animation: 500, |
| | | delay: 400, |
| | | delayOnTouchOnly: true, |
| | | handle: '.table-column-drag-icon ', |
| | | onEnd: (evt) => { |
| | | const { oldIndex, newIndex } = evt; |
| | | if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) { |
| | | return; |
| | | } |
| | | // Sort column |
| | | const columns = cloneDeep(plainSortOptions.value); |
| | | |
| | | if (oldIndex > newIndex) { |
| | | columns.splice(newIndex, 0, columns[oldIndex]); |
| | | columns.splice(oldIndex + 1, 1); |
| | | } else { |
| | | columns.splice(newIndex + 1, 0, columns[oldIndex]); |
| | | columns.splice(oldIndex, 1); |
| | | } |
| | | |
| | | plainSortOptions.value = columns; |
| | | setColumns(columns.filter((item) => state.checkedList.includes(item.value))); |
| | | }, |
| | | }); |
| | | // 记录原始order 序列 |
| | | sortableOrder = sortable.toArray(); |
| | | inited = true; |
| | | }); |
| | | } |
| | | |
| | | // Control whether the serial number column is displayed |
| | | function handleIndexCheckChange(e: CheckboxChangeEvent) { |
| | | isSetPropsFromThis = true; |
| | | isSetColumnsFromThis = true; |
| | | table.setProps({ |
| | | showIndexColumn: e.target.checked, |
| | | }); |
| | | } |
| | | |
| | | // Control whether the check box is displayed |
| | | function handleSelectCheckChange(e: CheckboxChangeEvent) { |
| | | isSetPropsFromThis = true; |
| | | isSetColumnsFromThis = true; |
| | | table.setProps({ |
| | | rowSelection: e.target.checked ? defaultRowSelection : undefined, |
| | | }); |
| | | } |
| | | |
| | | function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') { |
| | | if (!state.checkedList.includes(item.dataIndex as string)) return; |
| | | |
| | | const columns = getColumns().filter((c: BasicColumn) => |
| | | state.checkedList.includes(c.dataIndex as string), |
| | | ) 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; |
| | | } |
| | | updateSortOption(item); |
| | | table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed }); |
| | | setColumns(columns); |
| | | } |
| | | |
| | | function setColumns(columns: BasicColumn[] | string[]) { |
| | | isSetPropsFromThis = true; |
| | | isSetColumnsFromThis = true; |
| | | table.setColumns(columns); |
| | | const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => { |
| | | const visible = |
| | | columns.findIndex( |
| | | (c: BasicColumn | string) => |
| | | c === col.value || (typeof c !== 'string' && c.dataIndex === col.value), |
| | | ) !== -1; |
| | | return { dataIndex: col.value, fixed: col.fixed, visible }; |
| | | }); |
| | | |
| | | emit('columns-change', data); |
| | | } |
| | | |
| | | function getPopupContainer() { |
| | | return isFunction(attrs.getPopupContainer) |
| | | ? attrs.getPopupContainer() |
| | | : getParentContainer(); |
| | | } |
| | | |
| | | function updateSortOption(column: BasicColumn) { |
| | | plainSortOptions.value.forEach((item) => { |
| | | if (item.value === column.dataIndex) { |
| | | Object.assign(item, column); |
| | | // 排序 |
| | | if (oldIndex > newIndex) { |
| | | options.splice(newIndex, 0, options[oldIndex]); |
| | | options.splice(oldIndex + 1, 1); |
| | | } else { |
| | | options.splice(newIndex + 1, 0, options[oldIndex]); |
| | | options.splice(oldIndex, 1); |
| | | } |
| | | |
| | | // 更新 列可选项 |
| | | columnOptions.value = options; |
| | | |
| | | // 列表列更新 |
| | | tableColumnsUpdate(); |
| | | // 更新列缓存 |
| | | props.cache && columnOptionsSave(); |
| | | }, |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // 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); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 从缓存恢复 |
| | | const restore = () => { |
| | | if (typeof route.name === 'string') { |
| | | const isIndexColumnShowCache = tableSettingStore.getShowIndexColumn(route.name); |
| | | // 设置过才恢复 |
| | | if (typeof isIndexColumnShowCache === 'boolean') { |
| | | isIndexColumnShow.value = defaultIsIndexColumnShow && isIndexColumnShowCache; |
| | | } |
| | | |
| | | const isRowSelectionShowCache = tableSettingStore.getShowRowSelection(route.name); |
| | | // 设置过才恢复 |
| | | if (typeof isRowSelectionShowCache === 'boolean') { |
| | | isRowSelectionShow.value = defaultIsRowSelectionShow && isRowSelectionShowCache; |
| | | } |
| | | } |
| | | // 序号列更新 |
| | | onIndexColumnShowChange({ |
| | | target: { checked: isIndexColumnShow.value }, |
| | | } as CheckboxChangeEvent); |
| | | // 选择列更新 |
| | | onRowSelectionShowChange({ |
| | | target: { checked: isRowSelectionShow.value }, |
| | | } as CheckboxChangeEvent); |
| | | |
| | | if (typeof route.name === 'string') { |
| | | const cache = tableSettingStore.getColumns(route.name); |
| | | // 设置过才恢复 |
| | | if (Array.isArray(cache)) { |
| | | columnOptions.value = cache; |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 从 列可选项 更新 已选列 |
| | | 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, |
| | | } |
| | | : undefined, |
| | | }); |
| | | }; |
| | | |
| | | // 更新表单状态 |
| | | const formUpdate = () => { |
| | | // 从 列可选项 更新 已选列 |
| | | columnCheckedOptionsUpdate(); |
| | | |
| | | // 从 列可选项 更新 全选状态 |
| | | isColumnAllSelectedUpdate(); |
| | | |
| | | // 更新 showIndexColumn |
| | | showIndexColumnUpdate(isIndexColumnShow.value); |
| | | |
| | | // 更新 showRowSelection |
| | | showRowSelectionUpdate(isRowSelectionShow.value); |
| | | |
| | | // 列表列更新 |
| | | tableColumnsUpdate(); |
| | | }; |
| | | |
| | | 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, |
| | | }); |
| | | } |
| | | |
| | | return { |
| | | t, |
| | | ...toRefs(state), |
| | | indeterminate, |
| | | onCheckAllChange, |
| | | onChange, |
| | | plainOptions, |
| | | reset, |
| | | prefixCls, |
| | | columnListRef, |
| | | handleVisibleChange, |
| | | checkIndex, |
| | | checkSelect, |
| | | handleIndexCheckChange, |
| | | handleSelectCheckChange, |
| | | defaultRowSelection, |
| | | handleColumnFixed, |
| | | getPopupContainer, |
| | | }; |
| | | }, |
| | | // 默认值 缓存 |
| | | defaultIsIndexColumnShow = table.getBindValues.value.showIndexColumn || false; |
| | | defaultRowSelection = table.getRowSelection(); |
| | | defaultIsRowSelectionShow = !!defaultRowSelection; // 设置了 rowSelection 才出现 |
| | | defaultColumnOptions = options; |
| | | |
| | | // 默认值 赋值 |
| | | isIndexColumnShow.value = defaultIsIndexColumnShow; |
| | | isRowSelectionShow.value = defaultIsRowSelectionShow; |
| | | columnOptions.value = cloneDeep(options); |
| | | |
| | | // remove消失的列、push新出现的列 |
| | | props.cache && diff(); |
| | | |
| | | // 从缓存恢复 |
| | | props.cache && restore(); |
| | | |
| | | // 更新表单状态 |
| | | formUpdate(); |
| | | |
| | | isRestored = true; |
| | | } |
| | | }; |
| | | |
| | | // 初始化 |
| | | 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> |
| | | <style lang="less"> |
| | |
| | | transform: rotate(180deg); |
| | | } |
| | | |
| | | &__cloumn-list { |
| | | &__column-list { |
| | | svg { |
| | | width: 1em !important; |
| | | height: 1em !important; |
| | |
| | | } |
| | | |
| | | .ant-checkbox-group { |
| | | display: inline-block; |
| | | width: 100%; |
| | | min-width: 260px; |
| | | // flex-wrap: wrap; |