Is there a built-in tool in the Ant Vue Table component for making columns draggable? From my research, it seems there is no such feature available. I attempted to use vuedraggable, but encountered difficulties. Here is my table builder component. Do you have any suggestions or solutions?
<template>
<div v-if="_columns">
<div class="d-flex w-100 justify-content-end">
<div class="d-flex align-items-center">
<div v-if="filterColumn && _allColumns && _activeColumns"
class="mb-3 mx-1 mt-3 w-100 d-flex justify-content-end">
<div style="width: 200px">
<CustomSelect
:multiple="true"
:select-data="_allColumns"
v-model:value="_activeColumns"
placeholder="ستون ها"
@update:value="handleFilterColumnsSelectChange"
/>
</div>
</div>
<div v-if="additionalButtons" class="w-100 d-flex justify-content-end">
<a-space class="">
<template v-for="button in additionalButtons">
<a-button :class="'button button-' + button.color" @click.prevent="$emit(button.eventName)">
<i :class="button.icon"></i>
{{ button.label }}
</a-button>
</template>
</a-space>
</div>
</div>
</div>
<a-config-provider>
<template #renderEmpty>
<EmptyDb :simple="true"/>
</template>
<a-table
:scroll="{ x: 1500 }"
:data-source="_dataSource"
:columns="_columns"
:pagination="false"
size="small"
:loading="loading"
@change="tableChange"
:show-sorter-tooltip="false"
:row-selection="rowSelection ? { selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange }: false"
>
<template
#customFilterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }"
v-if="enableColumnSearch"
>
<div style="padding: 8px">
<a-input
v-model:value="searchInput"
:placeholder="` جستجوی ${column.title}`"
style="width: 188px; margin-bottom: 8px; display: block"
@pressEnter="handleColumnSearch(column)"
/>
<a-space>
<a-button
type="primary"
size="small"
style="width: 90px; margin-right: 8px"
@click="() => handleColumnSearch(column)"
>
<template #icon>
<i class="bi bi-search"></i>
</template>
جستجو
</a-button>
<a-button size="small" style="width: 90px" @click="() => searchInput = ''">
پاک کردن
</a-button>
</a-space>
</div>
</template>
<template #customFilterIcon="{ filtered }">
<SearchOutlined/>
</template>
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'actions'">
<td class="actions-cell d-flex justify-content-center text-center align-items-center">
<a-button size="small" @click="$emit('deleteClicked', record)" v-if="actions.deleteAction"
class="mx-1 button button-danger">
{{ buttonDeleteText ? buttonDeleteText : 'حذف' }}
</a-button>
<a-button size="small" @click="$emit('editClicked', record)" v-if="actions.editAction"
class="mx-1 button button-warning">
{{ buttonEditText ? buttonEditText : 'ویرایش' }}
</a-button>
<a-button size="small" @click="$emit('viewClicked', record)" v-if="actions.viewAction"
class="mx-1 button button-primary">
{{ buttonViewText ? buttonViewText : 'نمایش' }}
</a-button>
</td>
<td v-if="customActionButtons && customActionButtons.length > 0"
class="actions-cell d-flex justify-content-center text-center align-items-center">
<template v-for="button in customActionButtons">
<a-button class="ms-1" size="small" style="cursor: pointer"
@click.prevent="$emit(button.eventName, record, column)"
:class="'button button-' + button.color">
{{ button.label }}
</a-button>
</template>
</td>
</template>
<template v-else-if="column.key in columnRenders">
{{ columnRenders[column.key](record) }}
</template>
<template v-else>
<td class="text-center d-flex justify-content-center align-items-center">
{{ text }}
</td>
</template>
</template>
</a-table>
</a-config-provider>
<a-pagination
v-if="pagination"
:total="pagination*10"
v-model:current="currentPage"
@change="$emit('pagination', currentPage)"
class="d-flex justify-content-center mt-3"
:show-size-changer="false"
></a-pagination>
</div>
</template>
<script lang="ts">
import {defineComponent, type PropType, ref, reactive, computed, onMounted, watch, UnwrapRef} from "vue";
import type {TableColumnsType} from 'ant-design-vue';
import CustomSelect from "@/components/custom/global/CustomSelect.vue";
import EmptyDb from "@/components/custom/EmptyDb.vue";
import {SearchOutlined} from "@ant-design/icons-vue";
import draggable from "vuedraggable";
interface Actions {
deleteAction: boolean,
editAction: boolean,
viewAction: boolean
}
interface ColumnRenders {
[key: string]: (record: any) => string
}
export default defineComponent({
components: {CustomSelect, EmptyDb, SearchOutlined, draggable},
props: {
dataSource: {
required: true
},
columns: {
required: true
},
scrollX: {
required: false,
type: Number
},
actions: {
required: false,
type: Object as PropType<Actions>,
default: {
deleteAction: false,
editAction: false,
viewAction: false
}
},
pagination: {
type: Number,
required: false
},
loading: {
type: Boolean,
required: false
},
buttonEditText: {
type: Text,
required: false
},
buttonViewText: {
type: Text,
required: false
},
buttonDeleteText: {
type: Text,
required: false
},
rowKey: {
type: Boolean,
required: false
},
selectedRows: {
type: [],
required: false
},
rowSelection: {
type: Boolean,
required: false
},
filterColumn: {
type: Boolean,
default: false,
required: false
},
customActionButtons: {
type: Array as PropType<{ label: string, eventName: string, color: string }[]>,
required: false
},
columnRenders: {
type: Object as PropType<ColumnRenders>,
required: false,
default: () => ({})
},
actionCellWidth: {
type: Number,
required: false,
default: 300
},
enableColumnSearch: {
type: Boolean,
required: false,
default: false
},
additionalButtons: {
type: Array as PropType<{ label: string, eventName: string, color?: string, icon?: string }[]>,
required: false,
}
},
setup(props, context) {
type id = string | number;
const _allColumns = ref<any[] | null>(null);
const currentPage = ref<number | null>(1);
const _dataSource = ref(null);
const _columns = ref<TableColumnsType>(null);
const _columnsCopy = ref<TableColumnsType>(null);
const _activeColumns = ref<string[]>(null);
const editableData: UnwrapRef<Record<string, never>> = reactive({});
const searchInput = ref('');
const state = reactive<{
selectedRowKeys: id[];
loading: boolean;
}>({
selectedRowKeys: [], // Check here to configure the default column
loading: false,
});
// handle editable rows
// const edit = (key: string) => {
// editableData[key] = cloneDeep(_dataSource.value.filter(el => el.key === key)[0]);
// }
// const save = (key: string) => {
// Object.assign(_dataSource.value.filter(item => key === item.key)[0], editableData[key]);
// delete editableData[key];
// };
// const cancel = (key: string) => {
// delete editableData[key];
// };
const handleColumnSearch = (searchedColumn) => {
context.emit('columnSearch', searchedColumn, searchInput.value);
}
const onSelectChange = (selectedRowKeys: id[]) => {
state.selectedRowKeys = selectedRowKeys;
};
const hasSelected = computed(() => state.selectedRowKeys.length > 0);
const tableChange = (pagination, filters, sorter) => {
context.emit('handleTableChange', pagination, filters, sorter);
}
watch(() => props.dataSource, () => {
_dataSource.value = props.dataSource.map((el, index) => {
return {
index: index + 1,
...el
}
});
});
onMounted(() => {
_dataSource.value = props.dataSource.map((el, index) => {
return {
index: index + 1,
...el
}
});
let temp = props.columns.map(el => {
if (el.dataIndex === 'actions') {
return {
...el,
width: props.actionCellWidth,
fixed: 'right',
key: `column_${el.key}`
}
}
return {
...el,
width: 200,
}
});
_columns.value = [{
title: 'ردیف',
dataIndex: 'index',
key: 'index',
width: 70,
}, ...temp];
_columnsCopy.value = _columns.value;
_allColumns.value = _columns.value.map(el => {
return {
label: el.title,
value: el.key,
}
});
_activeColumns.value = _columns.value.map(el => el.key);
});
const handleFilterColumnsSelectChange = (values) => {
_columns.value = _columnsCopy.value.map(el => {
if (values.includes(el.key)) return el;
}).filter(el => el !== undefined);
}
return {
currentPage,
state,
hasSelected,
onSelectChange,
tableChange,
_columns,
_allColumns,
_activeColumns,
handleFilterColumnsSelectChange,
_dataSource,
searchInput,
handleColumnSearch,
// edit rows
// edit,
// save,
// cancel,
// editableData
}
}
})
</script>
<style lang="scss">
.cell-min-width {
text-align: center;
}
.ant-table-cell {
text-align: center !important;
}
:where(.css-dev-only-do-not-override-mv22ks).ant-table-wrapper .ant-table-thead > tr > th:not(:last-child):not(.ant-table-selection-column):not(.ant-table-row-expand-icon-cell):not([colspan])::before, :where(.css-dev-only-do-not-override-mv22ks).ant-table-wrapper .ant-table-thead > tr > td:not(:last-child):not(.ant-table-selection-column):not(.ant-table-row-expand-icon-cell):not([colspan])::before {
background-color: #909090;
}
.ant-table-thead th:last-child,
.ant-table-tbody td:last-child {
position: sticky !important;
left: -10px;
z-index: 1;
}
.ant-table-thead th:last-child {
box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px !important;
}
.ant-select-selection-overflow {
display: none !important;
}
.ant-table-thead {
.ant-table-cell {
font-size: 17px !important;
}
}
</style>
I tried using the vuedraggable component, but unfortunately, it didn't seem to work as expected.