好的,非常感謝您提供更詳細的項目情況。這是一個非常典型的父子組件通信場景。
根據您的新需求,我將對代碼進行重構:
FaultSelect.vue
(子組件): 這個組件現在將變得更加“純粹”。它只負責自身的下拉框邏輯,不關心外部按鈕,并通過props
接收可見性狀態,通過emits
將選擇結果通知父組件。ParentComponent.vue
(父組件): 這個組件將管理應用的狀態,包括按鈕、下拉框的可見性以及展示由子組件傳遞上來的數據。
這樣的結構使得組件職責單一,更易于維護和復用,完全符合大型項目的開發規范。
1. 子組件: FaultSelect.vue
這個組件現在只包含下拉框。它接收一個 visible
屬性來控制顯示,并通過 update:selectedOrder
事件將選中的完整對象發送出去。
<template><div v-if="visible" class="fault-select-container"><el-selectv-model="selectedOrderId"placeholder="請選擇故障工單"class="fault-select"filterable@change="handleSelectionChange"><el-optionv-for="order in faultOrders":key="order.cm_order_id":label="order.cm_order_id":value="order.cm_order_id"/></el-select></div>
</template><script setup>
import { ref, onMounted } from 'vue';
import { ElMessage } from 'element-plus';// --- Props & Emits 定義 ---/*** @description* defineProps 用于接收父組件傳遞的屬性。* 'visible': 控制此組件的顯示/隱藏。*/
const props = defineProps({visible: {type: Boolean,required: true,default: false,},
});/*** @description* defineEmits 用于聲明該組件會觸發哪些自定義事件。* 'update:selectedOrder': 當用戶選擇一個選項時觸發,將選中的完整工單對象傳遞給父組件。*/
const emit = defineEmits(['update:selectedOrder']);// --- 響應式狀態 ---/*** @description 存儲從后臺獲取的故障工單列表* @type {import('vue').Ref<Array<{cm_order_id: string, data: string}>>}*/
const faultOrders = ref([]);/*** @description 存儲當前選中的故障工單ID。它的狀態會在組件隱藏時被保留。* @type {import('vue').Ref<string | null>}*/
const selectedOrderId = ref(null);// --- 方法 ---/*** @description 模擬從后臺異步獲取故障數據*/
const fetchFaultOrders = async () => {try {// 模擬1秒網絡延遲await new Promise(resolve => setTimeout(resolve, 1000));// 模擬成功返回的數據const mockData = [{ cm_order_id: 'CM-20250708-0000047', data: '故障詳情47: 核心模塊溫度過高...' },{ cm_order_id: 'CM-20250708-0000021', data: '故障詳情21: 通信總線錯誤...' },{ cm_order_id: 'CM-20250708-0000025', data: '故障詳情25: 主電源電壓異常...' },];faultOrders.value = mockData;} catch (error) {ElMessage.error('獲取故障數據失敗!');console.error(error);}
};/*** @description 當下拉框選項變化時觸發* @param {string} selectedId - 當前選中的 cm_order_id*/
const handleSelectionChange = (selectedId) => {// 從列表中找到完整的工單對象const selectedOrderObject = faultOrders.value.find((order) => order.cm_order_id === selectedId);// 如果找到了,就通過 emit 事件將其發送給父組件if (selectedOrderObject) {emit('update:selectedOrder', selectedOrderObject);}
};// --- 生命周期鉤子 ---onMounted(() => {// 組件掛載后立即獲取數據,為用戶點擊“修復故障”做好準備fetchFaultOrders();
});
</script><style scoped>
/*將組件定位在右上角。父容器需要有 position: relative;
*/
.fault-select-container {position: absolute;top: 20px;right: 20px;z-index: 1000;background-color: #fff;padding: 10px;border-radius: 8px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}.fault-select {width: 260px;
}
</style>
2. 父組件: ParentComponent.vue
(示例)
這個父組件包含了控制按鈕,并負責調用子組件和處理子組件傳回的數據。
<template><div class="parent-container"><div class="main-content"><h1>地圖/主監控頁面</h1><div v-if="currentFault" class="fault-display-panel"><h3>故障詳情: {{ currentFault.cm_order_id }}</h3><pre>{{ currentFault.data }}</pre></div></div><div class="control-panel"><el-button type="primary" @click="openFaultRepair">修復故障</el-button><el-button @click="closeFaultRepair">故障恢復</el-button></div><FaultSelect:visible="isDropdownVisible"@update:selectedOrder="showData"/></div>
</template><script setup>
import { ref } from 'vue';
// 引入子組件
import FaultSelect from '../components/FaultSelect.vue';// --- 狀態定義 ---/*** @description 控制 FaultSelect 子組件是否可見* @type {import('vue').Ref<boolean>}*/
const isDropdownVisible = ref(false);/*** @description 存儲從子組件接收到的當前故障工單對象* @type {import('vue').Ref<{cm_order_id: string, data: string} | null>}*/
const currentFault = ref(null);// --- 方法定義 ---/*** @description 點擊“修復故障”按鈕時調用,顯示下拉框*/
const openFaultRepair = () => {isDropdownVisible.value = true;
};/*** @description 點擊“故障恢復”按鈕時調用,隱藏下拉框*/
const closeFaultRepair = () => {isDropdownVisible.value = false;
};/*** @description* 這是傳遞給子組件的回調方法。當子組件觸發 'update:selectedOrder' 事件時,此方法被調用。* @param {{cm_order_id: string, data: string}} selectedOrder - 子組件傳遞過來的完整工單對象*/
const showData = (selectedOrder) => {console.log('從子組件接收到數據:', selectedOrder);currentFault.value = selectedOrder;// 在這里可以執行更多操作,例如在地圖上高亮顯示故障位置等
};
</script><style scoped>
/* 父容器需要相對定位,以便子組件的絕對定位生效 */
.parent-container {position: relative;width: 100%;height: 100vh;background-color: #f0f2f5;
}.main-content {padding: 20px;
}/* 將控制按鈕固定在右上角,子組件會顯示在其下方或附近 */
.control-panel {position: absolute;top: 20px;right: 320px; /* 調整位置,給下拉框留出空間 */z-index: 1001; /* 比下拉框高一級,確保按鈕可點 */
}.fault-display-panel {margin-top: 20px;padding: 20px;border-radius: 8px;background-color: #fff;border: 1px solid #ebeef5;width: 400px;
}.fault-display-panel h3 {margin: 0 0 10px 0;
}.fault-display-panel pre {white-space: pre-wrap;word-wrap: break-word;background-color: #f9f9f9;padding: 10px;border-radius: 4px;
}
</style>
總結與優勢
這種重構方式是 Vue 開發中的標準實踐:
- 單一職責原則:
FaultSelect
只管下拉框,ParentComponent
只管業務流程和狀態。 - 單向數據流: 父組件通過
props
將狀態(visible
)向下傳遞給子組件。 - 事件驅動: 子組件通過
emits
將用戶操作的結果(selectedOrder
)向上傳遞給父組件。 - 高內聚,低耦合: 兩個組件之間的依賴性清晰明了,
FaultSelect
可以在項目的其他地方被輕松復用。 - 狀態保持:
selectedOrderId
狀態保留在子組件內部,當父組件再次設置:visible="true"
時,子組件會自然地顯示上次選中的值。同時,父組件的currentFault
也保留了上次選擇的數據,實現了完整的狀態記憶。