refactor(draggable): 优化拖拽组件绑定逻辑

- 添加 cloneBindingItem 方法用于创建唯一绑定键
- 使用 _bindingKey 替代 name 作为拖拽项标识符
- 修改 removeFromB 方法参数从 id 改为 index 提升删除效率
- 重构数据映射逻辑统一添加 _bindingKey 字段
- 移除重复的已绑定项过滤逻辑简化代码结构
This commit is contained in:
lixiaolong 2026-08-12 15:28:38 +08:00
parent 40a787e723
commit 9d57865e6a
3 changed files with 34 additions and 43 deletions

View File

@ -122,7 +122,8 @@
<div class="task-title">待选择点位</div> <div class="task-title">待选择点位</div>
<draggable :animation="340" :forceFallback="true" :list="taskLeftList" class="draggable-group" <draggable :animation="340" :forceFallback="true" :list="taskLeftList" class="draggable-group"
ghostClass="dragClass" :group="{ name: 'people', pull: 'clone', put: false}" itemKey="name"> ghostClass="dragClass" :group="{ name: 'people', pull: 'clone', put: false}"
:clone="cloneBindingItem" itemKey="_bindingKey">
<template #item="{ element, index }"> <template #item="{ element, index }">
<div class="list-group-item"> <div class="list-group-item">
<el-icon> <el-icon>
@ -138,11 +139,11 @@
<div style="height: 55vh"> <div style="height: 55vh">
<div class="task-title">点位排序</div> <div class="task-title">点位排序</div>
<draggable :animation="340" :forceFallback="true" :list="taskRightList" class="draggable-group" <draggable :animation="340" :forceFallback="true" :list="taskRightList" class="draggable-group"
ghostClass="dragClass" :group="{ name: 'people', pull: false, put: true }" itemKey="name"> ghostClass="dragClass" :group="{ name: 'people', pull: false, put: true }" itemKey="_bindingKey">
<template #item="{ element, index }"> <template #item="{ element, index }">
<div class="list-group-item"> <div class="list-group-item">
{{ index + 1 }} 、{{ element.waypointName }} {{ index + 1 }} 、{{ element.waypointName }}
<el-icon color="red" @click="removeFromB(element.id)" style="margin-left: 10px;"><Delete /></el-icon> <el-icon color="red" @click="removeFromB(index)" style="margin-left: 10px;"><Delete /></el-icon>
</div> </div>
</template> </template>
</draggable> </draggable>
@ -328,6 +329,9 @@ function resetBindPointQuery() {
} }
const taskId = ref(null); const taskId = ref(null);
let bindingSequence = 0
const createBindingKey = () => `waypoint-${Date.now()}-${bindingSequence++}`
const cloneBindingItem = (item) => ({ ...item, _bindingKey: createBindingKey() })
const handleBindPoint = async (row) => { const handleBindPoint = async (row) => {
dialogVisible.value = true; dialogVisible.value = true;
taskId.value = row.id; taskId.value = row.id;
@ -342,20 +346,14 @@ const handleBindPoint = async (row) => {
const getPointListData = async () => { const getPointListData = async () => {
const res = await getPointList(pointQueryParams) const res = await getPointList(pointQueryParams)
if (res.code === 200) { if (res.code === 200) {
let ids = [] taskLeftList.value = res.rows.map(item => ({ ...item, _bindingKey: createBindingKey() }))
if (taskRightList.value) {
ids = taskRightList.value.map(item => item.id)
}
taskLeftList.value = res.rows.filter(item => {
return !ids.includes(item.id)
})
} }
} }
const getBindPointByTaskId = async (taskId) => { const getBindPointByTaskId = async (taskId) => {
const res = await getWaypointByTaskId(taskId) const res = await getWaypointByTaskId(taskId)
if (res.code === 200) { if (res.code === 200) {
taskRightList.value = res.data; taskRightList.value = res.data.map(item => ({ ...item, _bindingKey: createBindingKey() }));
} }
} }
@ -379,9 +377,8 @@ const cancelPoint = () => {
dialogVisible.value = false; dialogVisible.value = false;
}; };
const removeFromB = (id) => { const removeFromB = (index) => {
const index = taskRightList.value.findIndex(item => item.id === id) taskRightList.value.splice(index, 1)
if (index !== -1) taskRightList.value.splice(index, 1)
} }
onMounted(() => { onMounted(() => {

View File

@ -108,7 +108,8 @@
<div class="task-title">待选择用例</div> <div class="task-title">待选择用例</div>
<draggable :animation="340" :forceFallback="true" :list="taskLeftList" class="draggable-group" <draggable :animation="340" :forceFallback="true" :list="taskLeftList" class="draggable-group"
ghostClass="dragClass" :group="{ name: 'people', pull: 'clone', put: false}" itemKey="name"> ghostClass="dragClass" :group="{ name: 'people', pull: 'clone', put: false}"
:clone="cloneBindingItem" itemKey="_bindingKey">
<template #item="{ element, index }"> <template #item="{ element, index }">
<div class="list-group-item"> <div class="list-group-item">
<el-icon> <el-icon>
@ -124,11 +125,11 @@
<div style="height: 55vh"> <div style="height: 55vh">
<div class="task-title">用例排序</div> <div class="task-title">用例排序</div>
<draggable :animation="340" :forceFallback="true" :list="taskRightList" class="draggable-group" <draggable :animation="340" :forceFallback="true" :list="taskRightList" class="draggable-group"
ghostClass="dragClass" :group="{ name: 'people', pull: false, put: true }" itemKey="name"> ghostClass="dragClass" :group="{ name: 'people', pull: false, put: true }" itemKey="_bindingKey">
<template #item="{ element, index }"> <template #item="{ element, index }">
<div class="list-group-item"> <div class="list-group-item">
{{ index + 1 }} 、{{ element.caseName }} {{ index + 1 }} 、{{ element.caseName }}
<el-icon color="red" @click="removeFromB(element.id)" style="margin-left: 10px;"><Delete /></el-icon> <el-icon color="red" @click="removeFromB(index)" style="margin-left: 10px;"><Delete /></el-icon>
</div> </div>
</template> </template>
</draggable> </draggable>
@ -302,6 +303,9 @@ function resetBindCaseQuery() {
} }
const taskId = ref(null); const taskId = ref(null);
let bindingSequence = 0
const createBindingKey = () => `case-${Date.now()}-${bindingSequence++}`
const cloneBindingItem = (item) => ({ ...item, _bindingKey: createBindingKey() })
const handleBindCase = async (row) => { const handleBindCase = async (row) => {
dialogVisible.value = true; dialogVisible.value = true;
taskId.value = row.id; taskId.value = row.id;
@ -315,20 +319,14 @@ const handleBindCase = async (row) => {
const getTestCaseListData = async () => { const getTestCaseListData = async () => {
const res = await getTestCaseList(caseQueryParams) const res = await getTestCaseList(caseQueryParams)
if (res.code === 200) { if (res.code === 200) {
let ids = [] taskLeftList.value = res.rows.map(item => ({ ...item, _bindingKey: createBindingKey() }))
if (taskRightList.value) {
ids = taskRightList.value.map(item => item.id)
}
taskLeftList.value = res.rows.filter(item => {
return !ids.includes(item.id)
})
} }
} }
const getbindCaseByTaskId = async (taskId) => { const getbindCaseByTaskId = async (taskId) => {
const res = await getTestCaseByTaskId(taskId) const res = await getTestCaseByTaskId(taskId)
if (res.code === 200) { if (res.code === 200) {
taskRightList.value = res.data; taskRightList.value = res.data.map(item => ({ ...item, _bindingKey: createBindingKey() }));
} }
} }
@ -352,9 +350,8 @@ const cancelCase = () => {
dialogVisible.value = false; dialogVisible.value = false;
}; };
const removeFromB = (id) => { const removeFromB = (index) => {
const index = taskRightList.value.findIndex(item => item.id === id) taskRightList.value.splice(index, 1)
if (index !== -1) taskRightList.value.splice(index, 1)
} }
onMounted(() => { onMounted(() => {

View File

@ -235,7 +235,8 @@
class="draggable-group" class="draggable-group"
ghostClass="dragClass" ghostClass="dragClass"
:group="{ name: 'people', pull: 'clone', put: false}" :group="{ name: 'people', pull: 'clone', put: false}"
itemKey="name" :clone="cloneBindingItem"
itemKey="_bindingKey"
> >
<template #item="{ element, index }"> <template #item="{ element, index }">
<div class="list-group-item"> <div class="list-group-item">
@ -258,12 +259,12 @@
class="draggable-group" class="draggable-group"
ghostClass="dragClass" ghostClass="dragClass"
:group="{ name: 'people', pull: false, put: true }" :group="{ name: 'people', pull: false, put: true }"
itemKey="name" itemKey="_bindingKey"
> >
<template #item="{ element, index }"> <template #item="{ element, index }">
<div class="list-group-item"> <div class="list-group-item">
{{ index + 1 }} 、{{ element.name }} {{ index + 1 }} 、{{ element.name }}
<el-icon color="red" @click="removeFromB(element.id)" style="margin-left: 10px;"><Delete /></el-icon> <el-icon color="red" @click="removeFromB(index)" style="margin-left: 10px;"><Delete /></el-icon>
</div> </div>
</template> </template>
</draggable> </draggable>
@ -395,6 +396,9 @@ const { queryParams, form, rules, paramsFormList } = toRefs(data);
const taskLeftList = ref([]); const taskLeftList = ref([]);
const taskRightList = ref([]); const taskRightList = ref([]);
let bindingSequence = 0;
const createBindingKey = () => `item-${Date.now()}-${bindingSequence++}`;
const cloneBindingItem = (item) => ({ ...item, _bindingKey: createBindingKey() });
/** 查询任务流程编排列表 */ /** 查询任务流程编排列表 */
function getList() { function getList() {
@ -537,17 +541,10 @@ const getLiftTaskList = () => {
return { return {
name: item.detectName, name: item.detectName,
id: item.id, id: item.id,
_bindingKey: createBindingKey(),
}; };
}) || []; }) || [];
if (taskRightList.value.length === 0) { taskLeftList.value = tempLeftData;
taskLeftList.value = tempLeftData;
return;
}
taskLeftList.value = tempLeftData.filter((item) => {
return !taskRightList.value.some((item2) => {
return item.id === item2.id;
});
});
}); });
}; };
//获取右侧编排数据 //获取右侧编排数据
@ -558,6 +555,7 @@ const getRightTaskList = () => {
return { return {
name: item.itemName, name: item.itemName,
id: item.itemId, id: item.itemId,
_bindingKey: createBindingKey(),
}; };
}) || []; }) || [];
getLiftTaskList(); getLiftTaskList();
@ -617,9 +615,8 @@ function handleGetSystemGroup(row) {
}); });
} }
const removeFromB = (id) => { const removeFromB = (index) => {
const index = taskRightList.value.findIndex(item => item.id === id) taskRightList.value.splice(index, 1)
if (index !== -1) taskRightList.value.splice(index, 1)
} }
onMounted(() => { onMounted(() => {