这里我尝试在 Primevue TreeTable 插槽中使用自定义逻辑,因为没有重新排序的道具。
这是 stackBlitz link 相同的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue TreeTable with Drag-and-Drop</title>
<link rel="stylesheet" href="https://unpkg.com/primevue/resources/themes/saga-blue/theme.css">
<link rel="stylesheet" href="https://unpkg.com/primevue/resources/primevue.min.css">
<link rel="stylesheet" href="https://unpkg.com/primeicons/primeicons.css">
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/primevue/umd/primevue.min.js"></script>
<script src="https://unpkg.com/primevue/umd/treetable/treetable.min.js"></script>
<script src="https://unpkg.com/primevue/umd/column/column.min.js"></script>
</head>
<body>
<div id="app"></div>
<script>
const { ref, onMounted } = Vue;
const TreeTable = PrimeVue.TreeTable;
const Column = PrimeVue.Column;
const App = {
components: {
TreeTable,
Column
},
setup() {
const nodes = ref([
{
key: '0',
data: { name: 'Applications', size: '100kb', type: 'Folder' },
children: [
{
key: '0-0',
data: { name: 'Vue', size: '25kb', type: 'Folder' },
children: [
{ key: '0-0-0', data: { name: 'vue.app', size: '10kb', type: 'Application' } },
{ key: '0-0-1', data: { name: 'native.app', size: '10kb', type: 'Application' } },
{ key: '0-0-2', data: { name: 'mobile.app', size: '5kb', type: 'Application' } }
]
},
{ key: '0-1', data: { name: 'editor.app', size: '25kb', type: 'Application' } },
{ key: '0-2', data: { name: 'settings.app', size: '50kb', type: 'Application' } }
]
},
{
key: '1',
data: { name: 'Cloud', size: '20kb', type: 'Folder' },
children: [
{ key: '1-0', data: { name: 'backup-1.zip', size: '10kb', type: 'Zip' } },
{ key: '1-1', data: { name: 'backup-2.zip', size: '10kb', type: 'Zip' } }
]
},
{
key: '2',
data: { name: 'Desktop', size: '150kb', type: 'Folder' },
children: [
{ key: '2-0', data: { name: 'note-meeting.txt', size: '50kb', type: 'Text' } },
{ key: '2-1', data: { name: 'note-todo.txt', size: '100kb', type: 'Text' } }
]
}
]);
const columns = ref([
{ field: 'name', header: 'Name', expander: true },
{ field: 'size', header: 'Size' },
{ field: 'type', header: 'Type' }
]);
const draggedNode = ref(null);
const onDragStart = (event, node) => {
draggedNode.value = node;
event.dataTransfer.effectAllowed = 'move';
};
const onDragOver = (event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
};
const onDrop = (event, dropNode) => {
event.preventDefault();
if (draggedNode.value && draggedNode.value !== dropNode) {
// Find and remove the dragged node
const removeNode = (nodes, node) => {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i] === node) {
return nodes.splice(i, 1)[0];
}
if (nodes[i].children) {
const result = removeNode(nodes[i].children, node);
if (result) return result;
}
}
};
const dragged = removeNode(nodes.value, draggedNode.value);
// Find the drop node and insert the dragged node
const insertNode = (nodes, node, dragged) => {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i] === node) {
if (!nodes[i].children) nodes[i].children = [];
nodes[i].children.push(dragged);
return;
}
if (nodes[i].children) {
insertNode(nodes[i].children, node, dragged);
}
}
};
insertNode(nodes.value, dropNode, dragged);
}
draggedNode.value = null;
};
return {
nodes,
columns,
onDragStart,
onDragOver,
onDrop
};
},
template: `
<div class="card">
<TreeTable :value="nodes" tableStyle="min-width: 50rem">
<Column v-for="col of columns" :key="col.field" :field="col.field" :header="col.header" :expander="col.expander">
<template #body="slotProps">
<div
draggable="true"
@dragstart="(event) => onDragStart(event, slotProps.node)"
@dragover="onDragOver"
@drop="(event) => onDrop(event, slotProps.node)"
>
{{ slotProps.node.data[col.field] }}
</div>
</template>
</Column>
</TreeTable>
</div>
`
};
Vue.createApp(App).mount('#app');
</script>
</body>
</html>