前提:因为ui展示的关系,用组件库不太方便
实现功能:
- 搜索
- 全选(取消全选)
- 全不选
- 全选
- 禁用
- 三级提示
界面
代码
1. 结构
<template>
<div class="search-box">
搜索类目:
<el-autocomplete
style="width: 300px"
class="inline-input"
v-model="searchVal"
:fetch-suggestions="querySearch"
placeholder="请输入内容"
:trigger-on-focus="false"
clearable
size="small"
@select="handleSelect"
/>
</div>
<div class="list">
<div class="item">
<div class="name">一级类目{{ chooseArr1.length }}</div>
<el-scrollbar class="content">
<ul>
<li
v-for="(item, index) in list1"
:key="item.id"
@click="check1(item, index)"
:class="{ active: index == index1 }"
>
<span @click.stop>
<el-checkbox
:indeterminate="isIndeterminate(item, 1)"
v-model="item.checked"
:disabled="!item.select"
@change="select1($event, item, index)"
/>
</span>
<span class="name">{{ item.name }}</span>
<el-icon><ArrowRight /></el-icon>
</li>
</ul>
</el-scrollbar>
</div>
<div class="item">
<div class="name">二级类目{{ chooseArr2.length }}</div>
<el-scrollbar class="content">
<ul>
<li
v-for="(item, index) in list2"
:key="item.id"
@click="check2(item, index)"
:class="{ active: index == index2 }"
>
<span @click.stop>
<el-checkbox
:indeterminate="isIndeterminate(item, 2)"
v-model="item.checked"
:disabled="!item.select"
@change="select2($event, item, index)"
/>
</span>
<span class="name">{{ item.name }}</span>
<el-icon><ArrowRight /></el-icon>
</li>
</ul>
</el-scrollbar>
</div>
<div class="item">
<div class="name">三级类目{{ chooseArr3.length }}</div>
<el-scrollbar class="content">
<ul>
<li
v-for="(item, index) in list3"
:key="item.id"
@click="check3(item, index)"
:class="{ active: index == index3 }"
>
<el-checkbox
v-model="item.checked"
:disabled="!item.select"
@change="select3($event, item, index)"
/>
<span class="name">{{ item.name }}</span>
<el-popover
v-if="item.appReminder"
placement="bottom-end"
width="200"
trigger="hover"
:content="item.appReminder.reason"
>
<template #reference>
<el-icon><QuestionFilled /></el-icon>
</template>
</el-popover>
</li>
</ul>
</el-scrollbar>
</div>
</div>
</template>
2. ts
<script lang="ts" setup name="menu">
import { ArrowRight, QuestionFilled } from "@element-plus/icons-vue";
import { getData } from "@/utils/getDate";
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
// 搜索
const searchVal = ref("");
const restaurants = ref<any>([]);
function querySearch(queryString: any, cb: any) {
let curRestaurants = restaurants.value;
let results = queryString
? curRestaurants.filter(createFilter(queryString))
: curRestaurants;
// 调用 callback 返回建议列表的数据
cb(results);
}
function createFilter(queryString: any) {
return (restaurant: any) => {
return (
restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) !== -1
);
};
}
function loadAll() {
let list: any[] = [];
list1.value.forEach((item1: any, index1: any) => {
item1.subCategories.forEach((item2: any, index2: any) => {
item2.subCategories.forEach((item3: any, index3: any) => {
list.push({
value: `${item1.name}>${item2.name}>${item3.name}`,
indexArr: [index1, index2, index3],
id: item3.id,
});
});
});
});
return list;
}
function handleSelect(item: any) {
index1.value = item.indexArr[0];
index2.value = item.indexArr[1];
index3.value = item.indexArr[2];
list2.value = list1.value[item.indexArr[0]].subCategories;
list3.value =
list1.value[item.indexArr[0]].subCategories[item.indexArr[1]].subCategories;
}
const list1 = ref<any>([]);
list1.value = getData();
restaurants.value = loadAll();
const list2 = ref<any>([]);
const list3 = ref<any>([]);
const index1 = ref(-1);
const index2 = ref(-1);
const index3 = ref(-1);
const chooseArr1 = ref<number[]>([]);
const chooseArr2 = ref<number[]>([]);
const chooseArr3 = ref<number[]>([]);
// 一级类目
function check1(item: any, idx: number) {
if (!item.select) {
ElMessage.closeAll();
return ElMessage.warning("已禁用");
}
index1.value = idx;
list2.value = item.subCategories;
list3.value = [];
}
function select1(e: any, item: any, idx: number) {
index1.value = idx;
item.checked = e;
item.subCategories.forEach((child: any) => {
if (child.select) {
child.checked = e;
child.subCategories.forEach((i: any) => {
if (i.select) {
i.checked = e;
} else {
i.checked = false;
}
});
} else {
child.checked = false;
}
child.subCategories = child.subCategories;
});
list2.value = item.subCategories;
list3.value = [];
}
function isIndeterminate(item: any, level: number) {
if (level == 1) {
let count1 = 0;
let count2 = 0;
item.subCategories.forEach((item1: any) => {
item1.subCategories.forEach((item2: any) => {
count1++;
if (item2.checked) {
count2++;
}
});
});
if (count1 === count2 || count2 === 0) {
count1 === count2 && count2 > 0
? (item.checked = true)
: (item.checked = false);
item.indeterminate = false;
return false;
} else {
item.indeterminate = true;
return true;
}
} else if (level == 2) {
let count1 = 0;
let count2 = 0;
item.subCategories.forEach((item1: any) => {
count1++;
if (item1.checked) {
count2++;
}
});
if (count1 === count2 || count2 === 0) {
count1 === count2 && count2 > 0
? (item.checked = true)
: (item.checked = false);
item.indeterminate = false;
return false;
} else {
item.indeterminate = true;
return true;
}
}
}
// 二级类目;
function check2(item: any, idx: number) {
if (!item.select) {
ElMessage.closeAll();
return ElMessage.warning("已禁用");
}
index2.value = idx;
list3.value = item.subCategories;
}
function select2(e: any, item: any, idx: number) {
index2.value = idx;
item.checked = e;
item.subCategories.forEach((child: any) => {
if (child.select) {
child.checked = e;
} else {
child.checked = false;
}
});
item.subCategories = item.subCategories;
list3.value = item.subCategories;
}
// 三级类目
function check3(item: any, idx: number) {
if (!item.select) {
ElMessage.closeAll();
return ElMessage.warning("已禁用");
}
index3.value = idx;
}
function select3(e: any, item: any, idx: number) {
if (!list2.value[index2.value].select) {
item.checked = false;
ElMessage.closeAll();
ElMessage.warning("已禁用");
return;
}
index3.value = idx;
}
// 处理选择的level数量
watch(
list1,
(newVal: any) => {
chooseArr1.value = [];
chooseArr2.value = [];
chooseArr3.value = [];
newVal.forEach((item1: any) => {
item1.subCategories.forEach((item2: any) => {
item2.subCategories.forEach((item3: any) => {
if (item3.checked) {
if (!chooseArr3.value.includes(item3.id)) {
chooseArr3.value.push(item3.id);
}
if (!chooseArr2.value.includes(item2.id)) {
chooseArr2.value.push(item2.id);
}
if (!chooseArr1.value.includes(item1.id)) {
chooseArr1.value.push(item1.id);
}
}
});
});
});
},
{ deep: true, immediate: true }
);
</script>
3. scss
<style lang="scss" scoped>
.list {
display: flex;
margin: auto;
width: 1000px;
justify-content: space-between;
.item {
width: calc((100% - 40px) / 3);
height: 100%;
.content {
border: 1px solid #ccc;
border-radius: 4px;
margin-top: 10px;
height: 200px;
box-sizing: border-box;
overflow-x: hidden;
overflow-y: auto;
}
}
}
ul {
list-style: none;
padding: 0;
margin: 0;
li {
display: flex;
height: 42px;
font-size: 14px;
align-items: center;
cursor: pointer;
padding: 0 10px;
border-bottom: 1px solid #e8e8e8;
&.active {
background: #ebf0ff;
}
.name {
margin-left: 10px;
margin-right: auto;
}
}
}
</style>
4. 模拟数据结构
export function getData() {
const data = [
{
fid: 0,
view: true,
select: true, //ture可选 false不可选
level: 1,
name: "珠宝首饰",
id: 6144,
checked: false, // 当前选中状态,绑定的值
subCategories: [
{
checked: false,
msg: null,
fid: 6144,
reminderCode: null,
select: true,
code: null,
level: 2,
pcReminder: null,
leaf: false,
subCategories: [
{
checked: false,
msg: null,
fid: 6145,
reminderCode: "open",
select: true,
code: null,
level: 3,
pcReminder: {
reason: "此类目不面向该店铺类型招商,$$$",
redirectList: [
{
redirectUrl:
"https://helpcenter.jd.com/vender/issue/44329.html",
redirectText: "查看规则",
redirectType: 1,
},
],
},
leaf: false,
subCategories: [
{
msg: null,
fid: 28212,
reminderCode: null,
select: true,
code: null,
level: 4,
pcReminder: null,
leaf: true,
subCategories: [],
view: true,
name: "宝宝转运珠",
appReminder: null,
risk: true,
id: 28213,
},
{
msg: null,
fid: 28212,
reminderCode: null,
select: false,
code: null,
level: 4,
pcReminder: null,
leaf: true,
subCategories: [],
view: true,
name: "宝宝金手链/手镯",
appReminder: null,
risk: true,
id: 28214,
},
{
msg: null,
fid: 28212,
reminderCode: null,
select: false,
code: null,
level: 4,
pcReminder: null,
leaf: true,
subCategories: [],
view: true,
name: "宝宝金锁",
appReminder: null,
risk: true,
id: 28215,
},
{
msg: null,
fid: 28212,
reminderCode: null,
select: false,
code: null,
level: 4,
pcReminder: null,
leaf: true,
subCategories: [],
view: true,
name: "宝宝金片",
appReminder: null,
risk: true,
id: 28216,
},
],
view: true,
name: "宝宝金饰",
appReminder: {
reason: "此类目不面向该店铺类型招商。",
redirectList: [
{
redirectUrl:
"https://helpcenter.jd.com/vender/issue/44329.html",
redirectText: "查看规则",
redirectType: 1,
},
],
},
risk: true,
id: 28212,
},
{
checked: false,
msg: null,
fid: 6145,
reminderCode: "open",
select: true,
code: null,
level: 3,
pcReminder: {
reason: "此类目不面向该店铺类型招商,$$$",
redirectList: [
{
redirectUrl:
"https://helpcenter.jd.com/vender/issue/44329.html",
redirectText: "查看规则",
redirectType: 1,
},
],
},
leaf: true,
subCategories: [],
view: true,
name: "黄金转运珠",
appReminder: {
reason: "此类目不面向该店铺类型招商。",
redirectList: [
{
redirectUrl:
"https://helpcenter.jd.com/vender/issue/44329.html",
redirectText: "查看规则",
redirectType: 1,
},
],
},
risk: true,
id: 13065,
},
],
view: true,
name: "黄金",
appReminder: null,
risk: true,
id: 6145,
},
{
checked: false,
msg: null,
fid: 6144,
reminderCode: null,
select: true,
code: null,
level: 2,
pcReminder: null,
leaf: false,
subCategories: [
{
checked: false,
msg: null,
fid: 6146,
reminderCode: "open",
select: true,
code: null,
level: 3,
pcReminder: {
reason: "此类目不面向该店铺类型招商,$$$",
redirectList: [
{
redirectUrl:
"https://helpcenter.jd.com/vender/issue/44329.html",
redirectText: "查看规则",
redirectType: 1,
},
],
},
leaf: true,
subCategories: [],
view: true,
name: "金片/金摆件",
appReminder: {
reason: "此类目不面向该店铺类型招商。",
redirectList: [
{
redirectUrl:
"https://helpcenter.jd.com/vender/issue/44329.html",
redirectText: "查看规则",
redirectType: 1,
},
],
},
risk: true,
id: 25579,
},
{
checked: false,
msg: null,
fid: 6146,
reminderCode: "open",
select: true,
code: null,
level: 3,
pcReminder: {
reason: "此类目不面向该店铺类型招商,$$$",
redirectList: [
{
redirectUrl:
"https://helpcenter.jd.com/vender/issue/44329.html",
redirectText: "查看规则",
redirectType: 1,
},
],
},
leaf: true,
subCategories: [],
view: true,
name: "特殊商品",
appReminder: {
reason: "此类目不面向该店铺类型招商。",
redirectList: [
{
redirectUrl:
"https://helpcenter.jd.com/vender/issue/44329.html",
redirectText: "查看规则",
redirectType: 1,
},
],
},
risk: true,
id: 13220,
},
],
view: true,
name: "金银投资",
appReminder: null,
risk: true,
id: 6146,
},
{
checked: false,
msg: null,
fid: 6144,
reminderCode: null,
select: true,
code: null,
level: 2,
pcReminder: null,
leaf: false,
subCategories: [
{
checked: false,
msg: null,
fid: 6155,
reminderCode: "success",
select: false,
code: null,
level: 3,
pcReminder: null,
leaf: true,
subCategories: [],
view: true,
name: "银手镯",
appReminder: null,
risk: false,
id: 11238,
},
{
checked: false,
msg: null,
fid: 6155,
reminderCode: "success",
select: false,
code: null,
level: 3,
pcReminder: null,
leaf: true,
subCategories: [],
view: true,
name: "银耳饰",
appReminder: null,
risk: false,
id: 13070,
},
],
view: true,
name: "银饰",
appReminder: null,
risk: false,
id: 6155,
},
],
},
{
fid: 0,
view: true,
select: false, //不可选
level: 1,
name: "医药",
id: 13314,
subCategories: [
{
msg: null,
fid: 13314,
reminderCode: null,
select: false,
code: null,
level: 2,
pcReminder: null,
leaf: false,
subCategories: [
{
msg: null,
fid: 30725,
reminderCode: "open",
select: true,
code: null,
level: 3,
pcReminder: {
reason: "此类目不面向该店铺类型招商,$$$",
redirectList: [
{
redirectUrl:
"https://helpcenter.jd.com/vender/issue/44329.html",
redirectText: "查看规则",
redirectType: 1,
},
],
},
leaf: true,
subCategories: [],
view: true,
name: "特殊商品",
appReminder: {
reason: "此类目不面向该店铺类型招商。",
redirectList: [
{
redirectUrl:
"https://helpcenter.jd.com/vender/issue/44329.html",
redirectText: "查看规则",
redirectType: 1,
},
],
},
risk: false,
id: 30726,
},
],
view: true,
name: "特殊商品",
appReminder: null,
risk: false,
id: 30725,
},
],
},
];
return data;
}