属性排序需求
国庆结束了第一波需求它来了,需求大致是这样的:
- 是一个拖拽排序的功能
- 同级子集是可以拖拽排序的, 现在是同一目标内,例如:A 容器不能退拽到 B 容器,只能在 A 容器内排序或者 B 容器内排序
如下图:

注:规格配置不能退拽到行业属性容器,只能在当前容器内排序,行业属性也是如此。
如何做
我的想法就很简单,将其封装成一个小组件,同时支持标题,描述,排序功能。
<!-- 父组件 -->
<script setup lang="ts">
import { ref } from "vue";
import attrDragItem from "./attrDragItem.vue";
const test = ref(
Array.from({ length: 7 }, (_, i) => ({
id: i + 1,
name: `C00${i + 1}${
["白色", "黑色", "红色", "绿色", "蓝色", "黄色", "紫色"][i]
}`,
elements: [],
}))
);
const test2 = ref([
{
id: 1,
name: "电机型号",
elements: [
{
id: 1,
name: "M2/V2",
},
{
id: 2,
name: "米家",
},
],
},
]);
</script>
<template>
<h3>颜色配置</h3>
<attrDragItem
:data="test"
:is-tree="false"
@update:data="newData => (test = newData)"
/>
<h3>规格配置</h3>
<attrDragItem
:data="test2"
:is-tree="true"
@update:data="newData => (test2 = newData)"
/>
<!-- .... 其他属性配置 -->
</template>attrDragItem就是封装的这个组件,它的 props 是:
data: 数据,数组格式is-tree: 是否是树状结构,默认false@update:data: 数据更新事件,返回新的数组
<!-- 子组件 attrDragItem.vue -->
<script setup lang="ts">
defineOptions({
name: "AttrDragItem",
});
import VabDraggable from "vuedraggable";
const props = defineProps<{
// 数据
data: TAny[];
// 是否是树结构
isTree: boolean;
}>();
const emit = defineEmits<{
"update:data": [value: any[]];
}>();
const dataList = computed({
get: () => props.data,
set: value => emit("update:data", value),
});
const dragOptions = computed(() => {
return {
animation: 600,
disabled: false,
ghostClass: "ghost",
};
});
// 处理子集元素的更新
const handleElementsUpdate = (item: TAny, newElements: TAny[]) => {
const index = dataList.value.findIndex(i => i.id === item.id);
if (index !== -1) {
const newData = [...dataList.value];
newData[index] = { ...item, elements: newElements };
emit("update:data", newData);
}
};
</script>
<template>
<vab-draggable
v-model="dataList"
v-bind="dragOptions"
item-key="id"
style="display: flex; flex-wrap: wrap; justify-content: flex-start"
:style="{ flexDirection: props.isTree ? 'column' : 'row' }"
>
<template #item="{ element: item }">
<div>
<div :class="['drag-item-container', props.isTree ? 'mb' : '']">
<vab-icon v-if="props.isTree" icon="draggable" />
<div :class="[props.isTree ? 'tree-item' : 'drag-item']">
{{ item.name }}
</div>
</div>
<template v-if="props.isTree">
<!-- 递归调用子组件 -->
<attrDragItem
:data="item.elements"
:is-tree="false"
@update:data="
newElements => handleElementsUpdate(item, newElements)
"
/>
</template>
</div>
</template>
</vab-draggable>
</template>
<style lang="scss" scoped>
.drag-item-container {
display: flex;
align-items: center;
color: #333;
&.mb {
margin-bottom: 12px;
}
.drag-item {
padding: 6px 12px;
background-color: rgba(238, 238, 238, 1);
border-radius: 50px;
margin-bottom: 8px;
margin-right: 8px;
cursor: move;
}
}
</style>