School Picker - daniel-qa/Vue GitHub Wiki

School Picker 組件

  • 特別注意,computed(), 要和 ref 的變數在同一支檔案,避免邏輯的混亂

  • 只引用需要的資料,有些函式,要改寫在子組件

  • 需要的數據: subItems

  • SchoolPicker.vue

<template>
    <div v-if="activeIndex !== null" class="list2 parent">
        <!-- 搜尋框 -->
        <el-input v-model="searchQuery"
                  placeholder="搜尋子項目..."
                  size="medium"
                  clearable
                  class="child"
                  style="width: 90%; top: 5%;"></el-input>

        <!-- 学校列表 -->
        <div class="list child" style="height: 85%; top: 12%; width: 90%;">
            <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>
</template>

<script setup>
    import { ref, computed, defineProps } from "vue";

    // 父組件傳入的 props
    const props = defineProps({
        activeIndex: {
            type: Number,
            required: true
        },
        activeIndex2: {
            type: Number,
            required: true
        },
        searchQuery: {
            type: String,
            required: true
        },
        filteredSubItems: {
            type: Array,
            required: true
        },
        setActive2: {
            type: Function,
            required: true
        },
        addToColumn3: {
            type: Function,
            required: true
        }
    });
</script>

<style scoped>
    /* 样式与原程序保持一致 */
    .list {
        width: 100%;
        height: 60vh;
        border: 1px solid #ccc;
        padding: 10px;
        overflow-y: auto;
    }

    .list2 {
        width: 100%;
        height: 60vh;
        border: 1px solid #ccc;
        padding: 10px;
    }

    .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;
    }

    /* 自定義 select 高度 */
    .custom-select {
        line-height: 1; /* 調整外部行高,避免過多空間 */
        margin: 0; /* 移除外部間距 */
    }

    .parent {
        position: relative; /* 父元素設置為 relative */
    }

    .child {
        position: absolute; /* 子元素設置為 absolute */
        top: 20px;
        left: 20px;
    }
</style>

props 在 Vue 中的作用類似於函式的參數,它是父組件傳遞給子組件的數據或方法。

  • 實際載入組件
<!-- 第二列:子级菜单 -->
<el-col :span="8">

	<SchoolPicker :activeIndex="activeIndex"
				:activeIndex2="activeIndex2"
				:searchQuery="searchQuery"
				:filteredSubItems="filteredSubItems"
				:setActive2="setActive2"
				:addToColumn3="addToColumn3" />
</el-col>
  • 載入設定
import SchoolPicker from "./SchoolPicker.vue"; // 引入新組件

// 选中状态
const activeIndex = ref(null); // 当前选中的学区
const activeIndex2 = ref(null); // 当前选中的学校
const searchQuery = 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 setActive2 = (index) => {
    activeIndex2.value = index;
};


// Type 種類
//1 .學區: area
//2 .地理資訊: geo
//3 .教育機構類型: unit
//4 .學校ID: school
//5 .醍摩豆ID: tmid

// 添加到第三列 (响应式触发更新)
const addToColumn3 = (item) => {
    //debugger;

    // 转为 JSON 字符串
    const newValue = JSON.stringify(item);

    // 检查是否已存在
    const isExist = column3Items.value.some((column3) => JSON.stringify(column3) === newValue);

    if (!isExist) {
        column3Items.value.push(item);
        ElMessage.success(`已加入接收名單`);
    } else {
        ElMessage.warning(`已存在接收名單中`);
    }
};

  • prop 變數的引用
props.activeIndex
⚠️ **GitHub.com Fallback** ⚠️