|
|
<template> <div class="productContent"> <div class="selectBox" @click="handelMenu" :class="{ 'active': isOpen }"> <span class="placeholder" :style="{ color: selectedItem ? '#333' : '#A8ABB2' }"> {{ selectedItem || placeholder }} </span> <span class="arrow"> <el-icon> <ArrowDown /> </el-icon> </span> </div>
<div class="menu" v-show="isOpen"> <div class="coinselect" @click="coinhandelMenu" :class="{ 'active': coinisOpen }"> <div class="cointxt"> 金币产品 </div> <span class="coin-arrow"> <el-icon> <ArrowDown /> </el-icon> </span> </div> <div class="coinoption" v-show="coinisOpen"> <el-radio v-model="selectedValue" label="金币充值" size="large" /> </div> <div class="product"> <div class="coinselect" @click="producthandelMenu" :class="{ 'active': productisOpen }"> <div class="cointxt"> 软件产品 </div> <span class="coin-arrow"> <el-icon> <ArrowDown /> </el-icon> </span> </div> <div class="productOption" v-show="productisOpen"> <hr class="line"> <div class="checktxt">软件</div> <div class="marketprodut"> <div class="fistlevel" v-for="(menu, index) in menuData" :key="menu.name" @click="clickmenu(index)" :class="{ 'selected': menu.options.includes(selectedValue) }"> <div class="label"> {{ menu.name }} <el-icon :class="{ 'rotate': activeIndex === index }"> <ArrowDown /> </el-icon> </div> <div v-show="activeIndex == index" class="selectoption" @click.stop> <el-radio-group v-model="selectedValue"> <div class="option" v-for="product in menu.options" :key="product"> <el-radio :label="product"> {{ product }} </el-radio> </div> </el-radio-group> </div> </div> </div> <hr class="line"> <div class="ai"> <div class="checktxt">AI机构探测神器</div> <hr class="line"> <el-radio-group v-model="selectedValue"> <el-radio v-for="ai in AIProduct" :key="ai" :label="ai" :value="ai"> {{ ai }} </el-radio> </el-radio-group> </div> <div class="ai"> <div class="checktxt">超级机构探测神器</div> <hr class="line"> <el-radio-group v-model="selectedValue"> <el-radio v-for="ai in superProduct" :key="ai" :label="ai" :value="ai"> {{ ai }} </el-radio> </el-radio-group> </div> <div class="ai"> <div class="checktxt">其他</div> <hr class="line"> <el-radio-group v-model="selectedValue"> <el-radio v-for="ai in InfoFee" :key="ai" :label="ai" :value="ai"> {{ ai }} </el-radio> </el-radio-group> </div> </div> </div> </div> </div></template><script setup>import { ref, watch, onMounted, computed, onUnmounted, nextTick } from 'vue';import { ArrowDown } from '@element-plus/icons-vue';
const searchData = ref('')const isOpen = ref(false)const coinisOpen = ref(false)const productisOpen = ref(false)const selectedItem = ref('')const dropdownRef = ref(null)const placeholder = ref('请选择产品')
const handelMenu = () => { isOpen.value = !isOpen.value ifselectAndOpen()}const coinhandelMenu = () => { coinisOpen.value = !coinisOpen.value}const producthandelMenu = () => { productisOpen.value = !productisOpen.value}
// 接收父组件通过 v-model 传入的值
const props = defineProps({ modelValue: { type: String, default: '' }})
const emit = defineEmits(['update:modelValue'])
const selectedValue = ref('')
watch(selectedValue, (newVal) => { emit('update:modelValue', newVal ? newVal : ''); selectedItem.value = newVal || '';});
const AIProduct = ['AI机构追踪', 'AI机构出击', 'AI机构资金', 'AI机活跃度','AI机构探测神器']const superProduct = ['超级机构透视', '超级机构伏击', '超级机构猎杀', '超级机构脉搏', '超级机构罗盘','超级机构探测神器']const InfoFee = ['静态信息费', '博股会员']const menuData = [ { name: '美股', options: ['美股软件', '美股金卡', '美股套餐'] }, { name: '港股', options: ['港股软件', '港股金卡', '港股套餐'] }, { name: 'A股', options: ['A股软件', 'A股金卡', 'A股套餐'] }, { name: '新加坡股', options: ['新加坡股软件', '新加坡股金卡', '新加坡股套餐'] }, { name: '马股', options: ['马股软件', '马股金卡', '马股套餐'] }, { name: '日本股', options: ['日本股软件', '日本股金卡', '日本股套餐'] }, { name: '泰股', options: ['泰股软件', '泰股金卡', '泰股套餐'] }, { name: '越南股', options: ['越南股软件', '越南股金卡', '越南股套餐'] }, { name: '印尼股', options: ['印尼股软件', '印尼股金卡', '印尼股套餐'] }, { name: '韩国股', options: ['韩国股软件', '韩国股金卡', '韩国股套餐'] }, { name: '台湾股', options: ['台湾股软件', '台湾股金卡', '台湾股套餐'] }];//全局事件实现点击外部关闭选项
const closeSoftwareSubmenu = () => { activeIndex.value = -1; // 将展开的二级菜单索引重置为 -1,实现关闭
};
const closeWholeDropdown = () => { isOpen.value = false; // 关闭整个弹窗
// 可选:同时关闭弹窗内的子面板(如金币产品、软件产品展开面板)
coinisOpen.value = false; productisOpen.value = false; closeSoftwareSubmenu(); // 同时关闭软件二级选项框
};
const handleGlobalClick = (e) => { // --- 原有:处理软件二级选项框外部关闭逻辑 ---
if (productisOpen.value) { const submenuContainers = document.querySelectorAll('.selectoption'); const firstLevelContainers = document.querySelectorAll('.fistlevel'); let isClickInsideSubmenu = false; let isClickInsideFirstLevel = false;
submenuContainers.forEach(container => container.contains(e.target) && (isClickInsideSubmenu = true)); firstLevelContainers.forEach(container => container.contains(e.target) && (isClickInsideFirstLevel = true));
if (!isClickInsideSubmenu && !isClickInsideFirstLevel) { closeSoftwareSubmenu(); } }
// --- 新增:处理整个弹窗外部关闭逻辑 ---
if (isOpen.value) { // 仅当弹窗处于打开状态时判断
// 获取整个下拉弹窗的 DOM 容器
const dropdownContainer = document.querySelector('.menu'); // 获取弹窗触发按钮(类名 .selectBox),点击按钮需正常切换弹窗,不触发关闭
const triggerButton = document.querySelector('.selectBox');
// 点击位置不在弹窗内,且不在触发按钮内 → 关闭整个弹窗
if (!dropdownContainer?.contains(e.target) && !triggerButton?.contains(e.target)) { closeWholeDropdown(); } }};
const ifselectAndOpen = async () => { await nextTick(); if (selectedValue.value == '金币充值') { coinisOpen.value = true } else if (selectedValue.value) { productisOpen.value = true } else { //不做处理
}}
//软件相关
const activeIndex = ref(-1)const clickmenu = (index) => { activeIndex.value = activeIndex.value === index ? -1 : index;}
const resetSelect = () => { selectedValue.value = ''; // 重置选中值
selectedItem.value = ''; // 重置显示文本
isOpen.value = false; // 关闭下拉菜单
coinisOpen.value = false; // 关闭金币产品子菜单
productisOpen.value = false; // 关闭软件产品子菜单
activeIndex.value = -1; // 关闭二级菜单
};
watch(() => props.modelValue, (newVal) => { selectedItem.value = newVal; selectedValue.value = newVal; }, { immediate: true });onMounted(async () => { console.log('打开组件', props.modelValue); selectedValue.value = props.modelValue; selectedItem.value = selectedValue.value || ''; document.addEventListener('click', handleGlobalClick); // 绑定全局事件
});onUnmounted(() => { selectedValue.value = ''; document.removeEventListener('click', handleGlobalClick); // 解绑全局事件
});defineExpose({ resetSelect });</script><style scoped lang="scss">.productContent { position: relative; width: 450px; font-family: 'Arial', sans-serif;}
.selectBox { border: 1px solid #e5e7eb; padding: 4px 12px; height: 23px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; background-color: #fff; border-radius: 6px; transition: all 0.3s ease;
.placeholder { flex: 1; font-size: 14px; line-height: 18px; color: #A8ABB2; }}
.arrow { margin-left: 8px; color: #999; transition: transform 0.3s ease;}
.selectBox.active .arrow { transform: rotate(180deg);}
.menu { position: absolute; top: 100%; left: 0; width: 160%; max-height: 700px; min-height: 200px; display: flex; padding: 10px; flex-direction: column; align-items: flex-start; gap: 10px; flex-shrink: 0; border-radius: 8px; background: #E4F0FC; box-shadow: 0 0 4px 0 #00000040; z-index: 100;
.coinselect { width: 100px; height: 20px; border: 1px solid #175BE5; padding: 5px 0 5px 12px; display: flex; border-radius: 5px;
.cointxt { width: 70px; height: 100%; display: flex; justify-content: center; align-items: center; color: #175be5; text-align: center; font-family: "PingFang SC"; font-size: 14px; font-style: normal; font-weight: 700; line-height: 20px; } }
.coin-arrow { flex: 1; display: flex; justify-content: center; align-items: center; color: #175BE5; }
.coinselect.active .coin-arrow { transform: rotate(-90deg); }
.product { width: 100%;
.line { display: flex; height: 1px; padding: 0 16px; align-items: flex-start; align-content: flex-start; gap: 8px 60px; flex-shrink: 0; align-self: stretch; flex-wrap: wrap; border-top: 1px solid #7E91FF; }
.checktxt { color: #5870ff; font-family: "PingFang SC"; font-size: 13px; font-style: normal; font-weight: 700; line-height: 22px; margin: 10px 20px; }
.productOption { width: 100%;
.ai { width: 100%; }
.marketprodut { width: 100%; display: flex; flex-wrap: wrap; gap: 10px;
.fistlevel { position: relative; width: 130px;
.label .el-icon { margin-left: 5px; transition: transform 0.3s ease; }
.label .rotate { transform: rotate(-90deg); // 箭头旋转效果
}
.label { margin-left: 10px; }
.selectoption { width: 100px; background-color: #fff; padding: 5px 20px; position: absolute; left: 60px; top: 0; z-index: 999; border: 1px solid #175BE5; border-radius: 6px;
:deep(.el-checkbox__label) { color: #333333; font-family: "PingFang SC"; font-size: 13px; font-style: normal; font-weight: 400; line-height: 22px; } } }
.fistlevel.selected .label { color: #175BE5; } } } }}</style>
|