初始化接口返回了全部数据,但只展示一级分类,点击一级分类出现二级分类,点击二级分类出现对应三级分类,效果如下:

再次点击一级分类,去取消二三级分类选中效果,但一级分类仍保持选中和展示,隐藏三级分类;点击二级分类,取消三级分类的选中和隐藏效果

如果子级分类列表为空,则点击父级分类,子级列表展示输入框

各级分类可以编辑与删除



三个分类列表一模一样,所以封装成一个组件,通过title传入名称,level来定该分类的级;pid为0表示一级,二级和三级数据的pid为其父级的id并传入。此外,还需要将一二三级不同的数据传入,并加入点击方法和数据更新方法。此外能看到,当鼠标移入或点击时,出现复选框。
另外,编辑与删除都是前端进行,只有一个保存接口,只将前端操作后的数据进行保存或更新。我们用name-level-pid来确定分类的唯一性,通过它进行删除或编辑,由于操作都在前端,所以需要克隆一份数据用来更新。
主要代码
<div
v-if="selectedCategories.length > 0 && !isGoogleCategory"
class="bulk-actions flex items-center justify-between py-2"
>
<span>{{ selectedCategories.length }} 已选</span>
<el-button
type="danger"
size="small"
@click="deleteSelectedCategories"
>删除</el-button
>
</div>
<!-- 分类列表 -->
<el-row>
<el-col :span="24">
<ul>
<li
v-for="category in categories"
:key="category.id"
:class="[
'category-item',
{
selected: category.selected,
'category-selected': category.id === props.selectedId
}
]"
@mouseover="hover = category.id"
@mouseleave="hover = null"
@click="selectCategory(category.id)"
>
<div class="item-content">
<el-checkbox
v-if="
(hover === category.id || category.selected) &&
editingId !==
`${category.name}-${category.level}-${category.pid}`
"
v-model="category.selected"
@click.stop
/>
<template
v-if="
editingId ===
`${category.name}-${category.level}-${category.pid}`
"
>
<div class="flex justify-between gap-4">
<el-form
:ref="
el =>
(editFormRefs[
`${category.name}-${category.level}-${category.pid}`
] = el)
"
class="flex flex-wrap gap-1"
:model="editForm"
:rules="rules"
style="width: 100%"
>
<el-form-item prop="name" class="w-10/12 !mb-0">
<el-input
:id="`edit-input-${category.name}-${category.level}-${category.pid}`"
v-model="editForm.name"
clearable
placeholder="类目名称"
@click.stop
@keyup.enter="saveEdit(category)"
@keyup.esc="cancelEdit"
/>
</el-form-item>
<div class="icon-buttons flex justify-end gap-2">
<el-icon :size="16" @click.stop="saveEdit(category)"
><Check
/></el-icon>
<el-icon :size="16" @click.stop="cancelEdit"
><Delete
/></el-icon>
</div>
<el-form-item prop="googleName" class="!mb-0 w-6/12">
<el-input
v-model="editForm.googleName"
size="small"
clearable
placeholder="Google类目"
@click.stop
/>
</el-form-item>
<el-form-item prop="typeName" class="!mb-0 w-5/12">
<el-input
v-model="editForm.typeName"
clearable
size="small"
placeholder="Product Type"
@click.stop
/>
</el-form-item>
</el-form>
</div>
</template>
<span v-else class="category-name">{{ category.name }}</span>
</div>
<el-dropdown
v-show="
hover === category.id &&
!isGoogleCategory &&
editingId !== category.id
"
trigger="hover"
:teleported="false"
>
<el-icon :size="20" class="more-icon"><More /></el-icon>
<template #dropdown>
<el-dropdown-menu
@mouseenter="menuVisible = true"
@mouseleave="menuVisible = false"
@click.stop
>
<!-- <el-dropdown-item @click="viewRelatedProducts(category)">
查看已关联商品
</el-dropdown-item> -->
<el-dropdown-item @click="editCategory(category)">
编辑分类
</el-dropdown-item>
<el-dropdown-item
style="color: red"
@click="openDeleteDialog(category)"
>
删除分类
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</li>
</ul>
</el-col>
</el-row>
</el-col>
</el-row>
<!-- 删除确认对话框 -->
<el-dialog
v-model="deleteDialogVisible"
width="30%"
align-center
:show-close="false"
:before-close="handleClose"
>
<template #header="{ close, titleId, titleClass }">
<div
class="my-header"
style="display: flex; justify-content: space-between"
>
<h4 :id="titleId" :class="titleClass">
确认删除
{{
selectedCategories.length > 1
? `${selectedCategories.length} 个分类`
: "分类"
}}
?
</h4>
<el-icon class="el-icon--left" style="cursor: pointer" @click="close"
><CloseBold
/></el-icon>
</div>
<ElDivider />
</template>
<div>
如果删除
<span style="color: black">
{{
selectedCategories.length > 1
? `${selectedCategories.length} 个所选分类`
: `${categoryToDelete?.name || ""}`
}}
</span>
,则该分类下的所有子分类也将被删除且删除分类将与产品解除关联。
</div>
<template v-slot:footer>
<span class="dialog-footer">
<ElDivider />
<el-button @click="handleClose">取消</el-button>
<el-button
type="danger"
:loading="deleting"
@click="confirmDeleteCategory"
>删除</el-button
>
</span>
</template>
</el-dialog>
使用方法
<div class="flex gap-2">
<CategoryComponent
title="一级分类"
:level="1"
pid="0"
:categoriesData="firstLevelCategories"
:isGoogleCategory="isGoogleCategory"
:parentSelected="true"
:selectedId="selectedFirstLevelId"
@updateCategories="updateFirstLevelCategories"
@categorySelected="handleFirstCategorySelected"
/>
<CategoryComponent
ref="secondCategoryComponent"
title="二级分类"
:level="2"
:pid="selectedFirstLevelId"
:categoriesData="secondLevelCategories"
:isGoogleCategory="false"
:parentSelected="!!selectedFirstLevelId"
:selectedId="selectedSecondLevelId"
@updateCategories="updateSecondLevelCategories"
@categorySelected="handleSecondCategorySelected"
/>
<CategoryComponent
ref="thirdCategoryComponent"
title="三级分类"
:level="3"
:pid="selectedSecondLevelId"
:isGoogleCategory="false"
:parentSelected="!!selectedSecondLevelId"
:selectedId="selectedThirdLevelId"
:categoriesData="thirdLevelCategories"
@updateCategories="updateThirdLevelCategories"
@categorySelected="handleThirdCategorySelected"
/>
</div>