You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

450 lines
12 KiB

<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机活跃度']
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>