div 選單關連模糊搜尋選單 - daniel-qa/Vue GitHub Wiki
-
1 .用戶點擊學區 → setActive(index) 設置選中學區 → activeIndex 更新。
-
2 .計算屬性 filteredSubItems 根據 activeIndex 和 searchQuery 過濾學校列表。
這裡使用 ref,動態響應數據,當改變 activeIndex 時,觸發 computed
計算公式 filteredSubItems ,進行數據的過濾,更新
- 3 .第二級菜單渲染學校列表。
用戶選擇學校 → setActive2(subIndex) 設置選中的學校。
加入接收名單 → addToColumn3() 將選中學校推入 column3Items。
<template>
<el-row style="background-color: #FFFFFF;">
<!-- 第一列:父级菜单 -->
<el-col :span="8">
<div class="list">
<div v-for="(item, index) in items"
:key="index"
:class="{'active': activeIndex === index}"
class="list-item"
@click="setActive(index)">
{{ item.name }}
{{ item.scCnt }} 校
{{ item.tchCnt }} 人
<el-button size="small" @click.stop="addToColumn3(item, 0)" type="success">加入</el-button>
</div>
</div>
</el-col>
<!-- 第二列:子级菜单 -->
<el-col :span="8">
<div v-if="activeIndex !== null" class="list2">
<!-- 搜尋框 -->
<el-input v-model="searchQuery"
placeholder="搜尋子項目..."
size="medium"
clearable
style="margin-bottom: 10px;"></el-input>
<!-- 学校列表 -->
<div class="list" style="height: 450px;">
<div v-for="(subItem, subIndex) in filteredSubItems"
:key="subIndex"
:class="{'active': activeIndex2 === subIndex}"
class="list-item"
@click="setActive2(subIndex)">
{{ subItem.name }}
{{ subItem.tchCnt }} 人
<el-button size="small" @click.stop="addToColumn3(subItem)" type="success">加入</el-button>
</div>
</div>
</div>
</el-col>
<!-- 第三列:展示加入的项 -->
<el-col :span="8">
<el-card v-if="false" style="height:600px;">
<template #header>
<span class="head-text">接收名單</span>
</template>
<div>
<el-scrollbar style="height: 300px; width:100% ;">
<el-list style="display: flex; flex-direction: column;">
<el-list-item v-for="(item, index) in column3Items"
:key="index"
class="el-list-item">
{{ item.name }}
{{ item.tchCnt }} 人
<div>
<!-- 标签部分 -->
<el-tag style="background-color: #409eff; color: white;">
{{ item.receiveType === 3 ? 'ID' : '学校' }}
</el-tag>
<el-button size="small" @click.stop="removeFromColumn3(index)">
<el-icon>
<CloseBold />
</el-icon>
</el-button>
</div>
</el-list-item>
</el-list>
</el-scrollbar>
</div>
</el-card>
</el-col>
</el-row>
<div v-if="false">
<!-- 获取学区数据按钮 -->
<el-button type="primary" @click="fetchData">获取学区信息</el-button>
</div>
</template>
<script setup>
import { ref, computed, onMounted, provide,inject } from "vue";
import { ElMessage } from "element-plus";
import axios from "axios";
import { CloseBold } from "@element-plus/icons-vue";
// 动态获取的学区和学校数据
const items = ref([]); // 学区列表(父级菜单)
const subItems = ref([]); // 对应的学校列表(子级菜单)
const scCntitems = ref([]); // 學區的校數列表(父级菜单數據)
const tchCntitems = ref([]); // 學區的學校人數(父级菜单數據)
// 选中状态
const activeIndex = ref(null); // 当前选中的学区
const activeIndex2 = ref(null); // 当前选中的学校
const searchQuery = ref(""); // 搜索框内容
// 从父组件注入的接收名单和更新方法
const column3Items = inject("column3Items", ref([])); // 接收名單
// 过滤子级菜单项(学校)
// filteredSubItems 是一个 计算属性,当 activeIndex 或 searchQuery 更新时会自动重新计算。
const filteredSubItems = computed(() => {
// 如果没有选中任何学区,返回空数组
if (activeIndex.value === null) return [];
// 获取当前选中的学区的学校列表
// 如果当前学区没有学校数据,则返回一个空数组
const currentSubItems = subItems.value[activeIndex.value] || [];
//debugger;
// 根据搜索框的内容过滤学校列表
// 使用 toLowerCase() 进行不区分大小写的匹配
return currentSubItems.filter((item) =>
item.name.toLowerCase().includes(searchQuery.value.toLowerCase())
);
});
const setActive = (index) => {
activeIndex.value = index; // 设置当前选中的学区索引
activeIndex2.value = null; // 重置第二级子菜单的选中状态
searchQuery.value = ""; // 清空子菜单的搜索框
};
// 选中学校
const setActive2 = (index) => {
activeIndex2.value = index;
};
// 添加到第三列 (响应式触发更新)
const addToColumn3 = (item) => {
column3Items.value.push(item);
//if (!column3Items.value.includes(item)) {
// column3Items.value.push(item);
// ElMessage.success(`${item} 已加入接收名單`);
//} else {
// ElMessage.warning(`${item} 已存在接收名單中`);
//}
};
// 从第三列移除
const removeFromColumn3 = (index) => {
column3Items.value.splice(index, 1);
};
// 获取学区和学校数据
const fetchData = async () => {
//設定 show 學校列表
const requestData = { showSchool: true };
try {
const response = await axios.post(
"https://localhost:5001/notice/get-areas",
requestData
);
const { state, result } = response.data;
if (state === 200) {
// 更新整合后的父级菜单数据
items.value = result.map((area) => ({
name: area.name,
scCnt: area.scCnt,
tchCnt: area.tchCnt,
receiveType: 0, // 0 學區,1 學校, 2 地理資訊
}));
//// 更新子级菜单(学校)
//subItems.value = result.map((area) =>
// area.schools.map((school) => school.name)
//
//);
// schools ,是Array欄位名稱
// 更新子级菜单(学校),存储完整信息
subItems.value = result.map((area) =>
area.schools.map((school) => ({
id: school.id,
name: school.name,
tchCnt: school.tchCnt,
receiveType: 1, // 0 學區,1 學校, 2 地理資訊
}))
);
//debugger;
console.log("学区信息获取成功!");
// ElMessage.success("学区信息获取成功!");
console.log("学区信息获取成功!");
} else {
ElMessage.error("获取学区数据失败,请稍后重试!");
}
} catch (error) {
console.error("请求失败:", error);
ElMessage.error("网络错误,请检查后重试!");
}
};
// 页面加载时自动获取数据
onMounted(fetchData);
</script>
<style scoped>
/* 样式与原程序保持一致 */
.list {
width: 100%;
height: 650px;
border: 1px solid #ccc;
padding: 10px;
overflow-y: auto;
}
.list2 {
width: 100%;
height: 650px;
border: 1px solid #ccc;
padding: 10px;
/*overflow-y: auto; 只差在沒有scroll */
}
.list-item {
min-height: 50px;
max-height: 100px;
line-height: 50px;
padding: 0 10px;
cursor: pointer;
transition: background-color 0.3s ease;
display: flex;
justify-content: space-between;
align-items: center;
}
.list-item:hover {
background-color: #f0f0f0;
}
.list-item.active {
background-color: #409eff;
color: white;
}
.head-text {
font-weight: bold;
font-size: 18px;
}
.el-list-item {
padding: 5px 10px !important;
line-height: 1.5 !important;
height: auto !important;
display: flex;
justify-content: space-between;
}
</style>