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
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>
|