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.
shengxian/components/wyb-drop-down/wyb-drop-down.vue

504 lines
13 KiB

2 years ago
<template>
<view
class="wyb-drop-down-box"
:style="{
'--duration': duration + 'ms',
'--autoContentTop': autoContentTop}">
<view class="wyb-drop-down-container" @tap.stop.prevent @touchmove.stop.prevent>
<view
class="wyb-drop-down-header"
:style="{
zIndex: zIndex,
backgroundColor: bgColor.header}">
<view
2 years ago
:class="dropOver?'wyb-drop-down-header-item dropdownTitle':'wyb-drop-down-header-item'"
2 years ago
v-for="(item,index) in options"
:key="item.header"
2 years ago
:style="{fontSize: fontSize.header + 'rpx',top:(statusBarHeight+navBarHeight+2)+'px'}">
<view :class="dropOver?'allType':''">
<view class="allTitle" v-if="dropOver">
全部分类
</view>
<view @tap.stop="onHeaderTap(index)" style="display: flex;flex-direction: column;align-items: center;justify-content: center;">
<view style="font-size:22rpx;font-weight:bold; writing-mode: tb-rl; letter-spacing: 8rpx;margin-bottom: 10rpx;">
{{!dropOver?'展开':'收起'}}
</view>
<text
class="wyb-drop-down-header-item-label"
:style="{
fontWeight: headerActiveIndex === index && dropOver && activeWeight ? 'bold' : 'normal',
color: headerActiveIndex === index && dropOver ? activeColor: defaultColor}">{{item.header}}</text>
<text
v-if="dropIcon === 'fill'"
class="iconfont icon-down-fill wyb-drop-down-header-item-icon"
:class="[headerActiveIndex === index && dropOver ? 'wyb-drop-down-header-item-icon-active' : '']"
:style="{
fontSize: fontSize.header - 5 + 'rpx',
color: headerActiveIndex === index && dropOver ? activeColor: defaultColor}" />
<text
v-if="dropIcon==='line'"
class="iconfont icon-down wyb-drop-down-header-item-icon"
:class="[headerActiveIndex === index && dropOver ? 'wyb-drop-down-header-item-icon-active' : '']"
:style="{
fontSize: fontSize.header - 5 + 'rpx',
transformOrigin: '50% 45%',
color: headerActiveIndex === index && dropOver ? activeColor: defaultColor}" ></text>
<view class="wyb-drop-down-vline" v-if="index !== options.length - 1" />
</view>
</view>
<!-- <text
2 years ago
class="wyb-drop-down-header-item-label"
:style="{
fontWeight: headerActiveIndex === index && dropOver && activeWeight ? 'bold' : 'normal',
color: headerActiveIndex === index && dropOver ? activeColor: defaultColor}">{{item.header}}</text>
<text
v-if="dropIcon === 'fill'"
class="iconfont icon-down-fill wyb-drop-down-header-item-icon"
:class="[headerActiveIndex === index && dropOver ? 'wyb-drop-down-header-item-icon-active' : '']"
:style="{
fontSize: fontSize.header - 5 + 'rpx',
color: headerActiveIndex === index && dropOver ? activeColor: defaultColor}" />
<text
v-if="dropIcon==='line'"
class="iconfont icon-down wyb-drop-down-header-item-icon"
:class="[headerActiveIndex === index && dropOver ? 'wyb-drop-down-header-item-icon-active' : '']"
:style="{
fontSize: fontSize.header - 5 + 'rpx',
transformOrigin: '50% 45%',
2 years ago
color: headerActiveIndex === index && dropOver ? activeColor: defaultColor}" ></text>
<view class="wyb-drop-down-vline" v-if="index !== options.length - 1" /> -->
2 years ago
</view>
</view>
2 years ago
<!-- height: autoHeight ? 'auto' : minHeight + 'rpx',
maxHeight: autoHeight && maxHeight ? maxHeight + 'rpx' : 'auto'} -->
2 years ago
<scroll-view
v-if="dropDown"
class="wyb-drop-down-content"
:class="[dropOver ? 'wyb-drop-down-content-active' : '']"
:scroll-y="scroll"
:enable-flex="true"
:scroll-anchoring="true"
:style="{
2 years ago
paddingTop:statusBarHeight+navBarHeight+70+'px',
2 years ago
zIndex: zIndex - 1,
fontSize: fontSize.content + 'rpx',
backgroundColor: bgColor.content,
borderBottomLeftRadius: radius + 'px',
borderBottomRightRadius: radius + 'px',
2 years ago
minHeight: minHeight + 'rpx'}
">
2 years ago
<view class="wyb-drop-down-content-box" v-for="(item,index) in options" :key="contentBoxKey(index)">
<view v-if="item['custom'] && headerActiveIndex === index && dropDown" class="wyb-drop-down-content-slot">
<slot></slot>
</view>
<view
v-if="!item['custom'] && headerActiveIndex === index && dropDown"
class="wyb-drop-down-content-item"
v-for="(content,zIndex) in item['contents']"
:key="content"
@tap.stop="onContentItemsTap(zIndex)">
<text
class="wyb-drop-down-content-item-label"
:style="{color: contentActiveIndexList[headerActiveIndex]['index'] === zIndex && dropOver ? activeColor: defaultColor}">
{{content}}
</text>
<text
v-if="contentActiveIndexList[headerActiveIndex]['index'] === zIndex && dropOver"
:style="{color: activeColor}"
class="iconfont icon-selected wyb-drop-down-content-item-icon" />
<view class="wyb-drop-down-line" v-if="zIndex !== options[headerActiveIndex].contents.length - 1" />
</view>
</view>
</scroll-view>
</view>
<view
v-if="dropDown"
class="wyb-drop-down-mask"
:class="[dropOver ? 'wyb-drop-down-mask-active' : '']"
@tap.stop="close"
@touchmove.stop.prevent
:style="{
zIndex: zIndex - 2,
height: screenHeight + 'px',
backgroundColor: 'rgba(0, 0, 0, ' + maskAlpha + ')'}" />
</view>
</template>
<script>
import shGridSwiper from '@/pages/index/category/item.vue';
export default {
components: {shGridSwiper},
data() {
return {
dropDown: false,
dropOver: false,
duration: 500,
contents: this.options[0].contents || [0],
headerActiveIndex: 0,
2 years ago
contentActiveIndexList: [],
statusBarHeight: 20,/* 状态栏高度 */
navBarHeight: 45,/* 导航栏高度 */
windowWidth: 375,/* 窗口宽度 */
2 years ago
}
},
computed: {
autoContentTop() {
return `${44 + this.rpxToPx(100)}px`
},
screenHeight() {
return uni.getSystemInfoSync().screenHeight
},
screenWidth() {
return uni.getSystemInfoSync().screenWidth
},
contentBoxKey() {
return function(index) {
return `option${index}`
}
}
},
props: {
categoryData:{
type:Array,
default(){
return []
}
},
options: {
type: Array,
default() {
return [{
header: 'A',
contents: ['1', '2']
}]
}
},
defaultIndexList: {
type: Array,
default() {
return []
}
},
autoHeight: {
type: Boolean,
default: true
},
minHeight: {
type: [String, Number],
default: 10
},
maxHeight: {
type: [String, Number],
default: 600
},
scroll: {
type: Boolean,
default: true
},
radius: {
type: [String, Number],
default: '0'
},
activeColor: {
type: String,
default: '#2979ff'
},
activeWeight: {
type: Boolean,
default: true
},
defaultColor: {
type: String,
default: '#333'
},
bgColor: {
type: Object,
default() {
return {
header: '#fff',
content: '#fff'
}
}
},
dropIcon: {
type: String,
default: 'fill'
},
fontSize: {
type: Object,
default() {
return {
header: 30,
content: 30
}
}
},
maskAlpha: {
type: [String, Number],
default: '0.5'
},
zIndex: {
type: Number,
default: 500
}
},
2 years ago
created(){
// 获取手机系统信息
const info = uni.getSystemInfoSync()
// 设置状态栏高度(H5顶部无状态栏小程序有状态栏需要撑起高度)
this.statusBarHeight = info.statusBarHeight
this.windowWidth = info.windowWidth
// 除了h5 app mp-alipay的情况下执行
// #ifndef H5 || APP-PLUS || MP-ALIPAY
// 获取胶囊的位置
const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
console.log(menuButtonInfo);
// (胶囊底部高度 - 状态栏的高度) + (胶囊顶部高度 - 状态栏内的高度) = 导航栏的高度
this.navBarHeight = (menuButtonInfo.bottom - info.statusBarHeight) + (menuButtonInfo.top - info.statusBarHeight)
this.windowWidth = menuButtonInfo.left
// #endif
},
2 years ago
mounted() {
if (this.defaultIndexList.length === 0) {
this.options.forEach((item, index) => {
if (!item.custom) {
this.contentActiveIndexList.push({headerIndex: index, index: 0})
} else {
this.contentActiveIndexList.push({headerIndex: index, custom: true})
}
})
} else {
let i = 0
this.options.forEach((item, index) => {
if (!item.custom) {
this.contentActiveIndexList.push([...this.defaultIndexList][i])
i++
} else {
this.contentActiveIndexList.push({headerIndex: index, custom: true})
}
})
}
},
methods: {
onHeaderTap(index) {
let item = this.options[index]
if (Object.is(this.headerActiveIndex, index) && this.dropOver) {
this.close()
} else {
this.headerActiveIndex = index
if (item.custom) {
this.$emit('change', {
headerIndex: index,
header: this.options[index].header
})
}
this.dropDown = true
this.$nextTick(() => {
this.dropOver = true
2 years ago
this.$emit('show',true)
2 years ago
})
}
},
onContentItemsTap(index) {
this.contentActiveIndexList[this.headerActiveIndex]['index'] = index
this.$forceUpdate()
let event = {
headerIndex: this.headerActiveIndex,
header: this.options[this.headerActiveIndex]['header'],
contentIndex: index,
content: this.options[this.headerActiveIndex]['contents'][index],
contentActiveIndexList: this.contentActiveIndexList
}
this.$emit('select', event)
},
close() {
this.dropOver = false
2 years ago
this.$emit('show',false)
2 years ago
setTimeout(() => {
this.dropDown = false
2 years ago
2 years ago
this.$emit('hide')
}, this.duration)
},
rpxToPx(rpx) {
return rpx / 750 * this.screenWidth
}
}
}
</script>
2 years ago
<style lang="scss">
2 years ago
@import './iconfont.css';
.wyb-drop-down-mask {
position: fixed;
top: 44px;
/* #ifndef H5 */
top: 0;
/* #endif */
left: 0;
bottom: 0;
right: 0;
opacity: 0;
transition: opacity var(--duration);
z-index: 498;
}
.wyb-drop-down-mask-active {
opacity: 1;
transition: opacity var(--duration);
}
.wyb-drop-down-header {
position: fixed;
top: 44px;
/* #ifndef H5 */
top: 0;
/* #endif */
left: 0;
right: 0;
display: flex;
flex-direction: row;
background-color: #fff;
z-index: 500;
}
.wyb-drop-down-header-item {
flex: 1;
2 years ago
height: 160rpx;
2 years ago
font-size: 30rpx;
2 years ago
/* border-bottom: 1px solid #eee; */
2 years ago
display: flex;
2 years ago
flex-direction: column;
2 years ago
align-items: center;
justify-content: center;
2 years ago
/* position: relative; */
position: absolute;
top:130rpx;
right: 0;
background:#ffffff;
padding: 0 20rpx;
}
.dropdownTitle{
background-color: transparent;
width:100%;
2 years ago
}
.wyb-drop-down-header-item-label {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
}
.wyb-drop-down-header-item-icon {
2 years ago
/* margin-right: 16rpx; */
2 years ago
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
transform-origin: 50% 40%;
transform: rotate(0);
transition: transform var(--duration);
2 years ago
font-size:24rpx;
2 years ago
}
.wyb-drop-down-header-item-icon-active {
transform: rotate(180deg);
transition: transform var(--duration);
}
.wyb-drop-down-content {
position: fixed;
top: var(--autoContentTop);
2 years ago
/* background:red !important; */
2 years ago
/* #ifndef H5 */
2 years ago
top: 0rpx;
2 years ago
/* #endif */
left: 0;
right: 0;
2 years ago
z-index: 999;
2 years ago
display: flex;
flex-direction: column;
align-items: flex-start;
background-color: #fff;
transform: translateY(-100%);
transition: transform var(--duration);
2 years ago
padding-top: 270rpx;
padding-bottom: 50rpx;
background-image: linear-gradient(to bottom, #d1f4e3 0%, #ffffff 100%);
2 years ago
}
.wyb-drop-down-content-active {
transform: translateY(0);
transition: transform var(--duration);
}
.wyb-drop-down-content-item {
width: 100%;
height: 100rpx;
font-size: 30rpx;
display: flex;
flex-direction: column;
position: relative;
}
.wyb-drop-down-content-item-label {
width: 90%;
height: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
padding-left: 50rpx;
}
.wyb-drop-down-content-item-icon {
position: absolute;
top: 50%;
right: 40rpx;
font-size: 40rpx;
transform: translateY(-50%);
}
.wyb-drop-down-content-box {
width: 100%;
}
.wyb-drop-down-content-slot {
width: 100%;
height: 100%;
}
.wyb-drop-down-vline {
width: 1px;
height: 40rpx;
background-color: #eee;
position: absolute;
right: 0;
}
.wyb-drop-down-line {
width: 100%;
height: 1px;
2 years ago
/* background-color: #eee; */
2 years ago
margin-left: 50rpx;
}
2 years ago
.allType{
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.allTitle{
font-weight:bold;
font-size:28rpx;
}
}
2 years ago
</style>