2 Commits
fd791b6f6a
...
58b4e1c618
Author | SHA1 | Message | Date |
---|---|---|---|
|
58b4e1c618 |
产品选择组件
|
1 day ago |
|
3d98845145 |
新增付款币种组件
|
2 days ago |
3 changed files with 508 additions and 22 deletions
-
336src/components/MoneyManage/CurrencySelect.vue
-
121src/components/MoneyManage/ProductSelect.vue
-
71src/views/moneyManage/receiveDetail/receiveDetail.vue
@ -0,0 +1,336 @@ |
|||
<template> |
|||
<div class="dropdown" ref="dropdownRef"> |
|||
<!-- 下拉框触发器 --> |
|||
<div class="dropdown-toggle" @click="toggleMenu" :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="dropdown-menu" v-if="isOpen"> |
|||
<!-- 搜索框 --> |
|||
<div class="search"> |
|||
<input type="text" v-model="searchData" class="search-input" placeholder="查询" @focus="handleSearchFocus" |
|||
@blur="handleSearchBlur"> |
|||
<el-icon v-show="!searchData" class="search-icon"> |
|||
<Search /> |
|||
</el-icon> |
|||
<el-icon class="clear-icon" v-if="searchData" @click="clearSearch"> |
|||
<CircleClose /> |
|||
</el-icon> |
|||
</div> |
|||
|
|||
<!-- 选项区域:按钮样式 --> |
|||
<div class="menuContent"> |
|||
<button class="dropdown-item" v-for="(item, index) in filteredItems" :key="index" @click="handleSelect(item)" |
|||
:class="{ 'selected': selectedItem === item }"> |
|||
{{ item }} |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref, computed, watchEffect, onMounted } from 'vue'; |
|||
|
|||
const searchData = ref('') |
|||
const isOpen = ref(false) |
|||
const selectedItem = ref('') |
|||
const dropdownRef = ref(null) |
|||
|
|||
const props = defineProps({ |
|||
items: { |
|||
type: Array, |
|||
required: true, |
|||
default: () => [] |
|||
}, |
|||
placeholder: { |
|||
type: String, |
|||
default: '请选择支付方式' |
|||
}, |
|||
modelValue: { |
|||
type: String, |
|||
default: '' |
|||
} |
|||
}) |
|||
|
|||
const emit = defineEmits(['update:modelValue', 'change']) |
|||
|
|||
// 切换下拉菜单 |
|||
const toggleMenu = () => { |
|||
isOpen.value = !isOpen.value |
|||
searchData.value = '' |
|||
} |
|||
|
|||
// 清除搜索 |
|||
const clearSearch = () => { |
|||
searchData.value = '' |
|||
} |
|||
|
|||
// 选择选项 |
|||
const handleSelect = (item) => { |
|||
selectedItem.value = item |
|||
isOpen.value = false |
|||
emit('update:modelValue', item) |
|||
emit('change', item) |
|||
} |
|||
|
|||
// 点击外部关闭菜单 |
|||
const handleClickOutside = (event) => { |
|||
if (dropdownRef.value && !dropdownRef.value.contains(event.target)) { |
|||
isOpen.value = false |
|||
} |
|||
} |
|||
|
|||
// 监听搜索框的焦点和失焦事件 |
|||
const handleSearchFocus = () => { |
|||
// 可以在这里添加额外的逻辑 |
|||
} |
|||
|
|||
const handleSearchBlur = () => { |
|||
// 可以在这里添加额外的逻辑 |
|||
} |
|||
|
|||
// 搜索过滤 |
|||
const filteredItems = computed(() => { |
|||
if (!searchData.value) return props.items |
|||
return props.items.filter(item => |
|||
item.toLowerCase().includes(searchData.value.toLowerCase()) |
|||
) |
|||
}) |
|||
|
|||
// 挂载/卸载事件 |
|||
onMounted(() => { |
|||
document.addEventListener('click', handleClickOutside) |
|||
return () => { |
|||
document.removeEventListener('click', handleClickOutside) |
|||
} |
|||
}) |
|||
|
|||
// 监听外部值变化 |
|||
watchEffect(() => { |
|||
selectedItem.value = props.modelValue |
|||
}) |
|||
</script> |
|||
<style scoped lang="scss"> |
|||
// 下拉容器 |
|||
.dropdown { |
|||
position: relative; |
|||
width: 268px; |
|||
font-family: 'Arial', sans-serif; |
|||
} |
|||
|
|||
// 触发器:控制展开/收起 |
|||
.dropdown-toggle { |
|||
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; |
|||
} |
|||
} |
|||
|
|||
// 展开状态:边框+阴影高亮 |
|||
.dropdown-toggle.active { |
|||
border-color: #678BFF; |
|||
box-shadow: 0 0 0 2px rgba(103, 139, 255, 0.1); |
|||
} |
|||
|
|||
// 箭头图标:展开时旋转 |
|||
.arrow { |
|||
margin-left: 8px; |
|||
color: #999; |
|||
transition: transform 0.3s ease; |
|||
} |
|||
|
|||
.dropdown-toggle.active .arrow { |
|||
transform: rotate(180deg); |
|||
} |
|||
|
|||
// 下拉菜单主体 |
|||
.dropdown-menu { |
|||
position: absolute; |
|||
top: 100%; // 紧贴触发器下方 |
|||
left: 0; |
|||
width: 100%; |
|||
border: 1px solid #678BFF; |
|||
max-height: 300px; |
|||
background-color: #fff; |
|||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
|||
border-radius: 0 0 8px 8px; // 仅底部两侧圆角 |
|||
z-index: 1000; |
|||
margin-top: 15px; // 关键:不要有上外边距,尖角要紧贴触发器 |
|||
overflow: visible; |
|||
|
|||
|
|||
&::before { |
|||
content: ""; |
|||
position: absolute; |
|||
top: -8px; |
|||
left: 50%; |
|||
transform: translateX(-50%) scaleY(0.5); |
|||
width: 30px; |
|||
height: 16px; |
|||
background: #fff; |
|||
clip-path: polygon(0 100%, 100% 100%, 50% 0); |
|||
z-index: 1001; |
|||
border: none; |
|||
/* 移除原来的边框 */ |
|||
} |
|||
|
|||
&::after { |
|||
content: ""; |
|||
position: absolute; |
|||
top: -9px; |
|||
/* 比 ::before 往下一点,制造边框效果 */ |
|||
left: 50%; |
|||
transform: translateX(-50%) scaleY(0.5); |
|||
width: 30px; |
|||
height: 16px; |
|||
background: #678BFF; |
|||
clip-path: polygon(0 100%, 100% 100%, 50% 0); |
|||
z-index: 1000; |
|||
} |
|||
} |
|||
|
|||
// 搜索框区域 |
|||
.search { |
|||
position: sticky; |
|||
top: 0; |
|||
background-color: #FFFFFF; |
|||
z-index: 1002; |
|||
padding: 10px 14px 0px 10px; |
|||
} |
|||
|
|||
// 搜索输入框:浅灰背景 + 图标定位 |
|||
.search-input { |
|||
width: 100%; |
|||
height: 27px; |
|||
padding: 0 12px 0 5px; |
|||
/* 左侧留出图标空间 */ |
|||
border: 1px solid #dcdfe6; |
|||
border-radius: 10px; |
|||
box-sizing: border-box; |
|||
background-color: #f8f9fa; |
|||
/* 浅灰背景匹配参考图 */ |
|||
outline: none; |
|||
font-size: 12px; |
|||
transition: border-color 0.3s ease; |
|||
|
|||
&::placeholder { |
|||
color: #909399; |
|||
} |
|||
|
|||
&:hover { |
|||
border-color: #c0c4cc; |
|||
} |
|||
|
|||
&:focus { |
|||
border-color: #678BFF; |
|||
} |
|||
} |
|||
|
|||
// 搜索图标:左侧定位 |
|||
.search-icon { |
|||
position: absolute; |
|||
top: 62%; |
|||
left: 50px; |
|||
transform: translateY(-50%); |
|||
color: #909399; |
|||
z-index: 1003; |
|||
} |
|||
|
|||
// 清除图标:右侧定位 + hover效果 |
|||
.clear-icon { |
|||
position: absolute; |
|||
top: 62%; |
|||
right: 20px; |
|||
transform: translateY(-50%); |
|||
color: #909399; |
|||
cursor: pointer; |
|||
z-index: 1003; |
|||
|
|||
&:hover { |
|||
color: #606266; |
|||
} |
|||
} |
|||
|
|||
// 选项容器:调整滚动区域高度 |
|||
.menuContent { |
|||
max-height: 200px; |
|||
/* 减去搜索框高度和尖角高度 */ |
|||
overflow-y: auto; |
|||
padding: 8px; |
|||
padding: 10px 14px 12px 10px; |
|||
} |
|||
|
|||
// 选项按钮:无边框 + hover/选中效果 |
|||
.dropdown-item { |
|||
width: 100%; |
|||
height: 27px; |
|||
padding: 5px 12px 5px 5px; |
|||
text-align: center; |
|||
cursor: pointer; |
|||
border: none; |
|||
border-radius: 10px; |
|||
background-color: #fff; |
|||
font-size: 12px; |
|||
font-style: normal; |
|||
font-weight: 700; |
|||
line-height: 20px; |
|||
margin: 5px 0; |
|||
color: #040A2D; |
|||
transition: all 0.2s ease; |
|||
|
|||
&:hover { |
|||
background-color: #F3FAFE; |
|||
/* hover浅灰 */ |
|||
} |
|||
|
|||
&.selected { |
|||
background-color: #E5EBFE; |
|||
/* 选中浅蓝 */ |
|||
color: #2741DE; |
|||
/* 选中文字蓝色 */ |
|||
} |
|||
} |
|||
|
|||
// 可选:滚动条美化(与原风格一致) |
|||
.menuContent::-webkit-scrollbar { |
|||
width: 6px; |
|||
} |
|||
|
|||
.menuContent::-webkit-scrollbar-track { |
|||
background: #f1f1f1; |
|||
border-radius: 3px; |
|||
} |
|||
|
|||
.menuContent::-webkit-scrollbar-thumb { |
|||
background: #c0c4cc; |
|||
border-radius: 3px; |
|||
|
|||
&:hover { |
|||
background: #909399; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,121 @@ |
|||
<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="coin"> |
|||
<div class="coinselect"> |
|||
coin1 |
|||
<span class="coin-arrow"> |
|||
<el-icon> |
|||
<ArrowDown /> |
|||
</el-icon> |
|||
</span> |
|||
</div> |
|||
<div class="coinoption"> |
|||
coIn2 |
|||
</div> |
|||
</div> |
|||
<div class="product"> |
|||
22 |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup> |
|||
import { ref, computed, watchEffect, onMounted } from 'vue'; |
|||
const searchData = ref('') |
|||
const isOpen = ref(false) |
|||
const selectedItem = ref('') |
|||
const dropdownRef = ref(null) |
|||
const placeholder = ref('请选择产品') |
|||
|
|||
const handelMenu = () => { |
|||
isOpen.value = !isOpen.value |
|||
} |
|||
</script> |
|||
<style scoped lang="scss"> |
|||
.productContent { |
|||
position: relative; |
|||
width: 268px; |
|||
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: 130%; |
|||
max-height: 600px; |
|||
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; |
|||
|
|||
|
|||
.coin { |
|||
width: 100%; |
|||
|
|||
.coinselect { |
|||
width: 100px; |
|||
height: 30px; |
|||
border: 1px solid #175BE5; |
|||
padding: 5px 0 5px 12px; |
|||
display: flex; |
|||
|
|||
.coin-arrow { |
|||
margin-top: 8px; |
|||
color: #111; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue