<template>
|
<view class="option" :style="{'width': width,'height': height}" ref="dropdown">
|
<view class="option-select-title" @click="fnShowOptionList()">
|
<input class="inp-select" placeholder="请选择" :value="value" disabled />
|
<!-- 箭头图标 -->
|
<view class="trans" :class="showOptionList?'trans-from':''">
|
<um-icon name="down"></um-icon>
|
</view>
|
</view>
|
<!-- 下拉列表 -->
|
<view class="option-list" :style="[{height: ListHeightVal}, addStyle(listStyle)]">
|
<view class="option-list-padding">
|
<block v-for="(item, index) in optionList" :key="index">
|
<view class="option-item" :style="[addStyle(selectItemIdx == index ? selectedItemStyle: itemStyle)]"
|
@click.stop="fnChangeOption(item, index)">
|
{{rangeKey? item[rangeKey]: item}}
|
</view>
|
</block>
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script>
|
/**
|
* @param optionList {Array} 下拉列表数据
|
* @example <um-dropdown :optionList="optionList"></um-dropdown>/>
|
*/
|
export default {
|
props: {
|
// 菜单选择中时的样式
|
itemStyle: {
|
type: [String, Object],
|
default: () => ({
|
color: '#333',
|
fontSize: '28rpx'
|
})
|
},
|
// 菜单非选中时的样式
|
selectedItemStyle: {
|
type: [String, Object],
|
default: () => ({
|
color: '#2973F8',
|
fontSize: '28rpx',
|
background: '#F5F7FA'
|
})
|
},
|
// 自定义列表样式
|
listStyle: {
|
type: [String, Object],
|
default: '',
|
},
|
// 列表数据
|
optionList: {
|
type: Array,
|
default: function() {
|
return []
|
}
|
},
|
// 选择框的宽度
|
width: {
|
type: String,
|
default: '100%'
|
},
|
// 选择框的高度
|
height: {
|
type: String,
|
default: '100%'
|
},
|
// 如果数组包含对象时,需要显示的key值
|
rangeKey: {
|
type: String,
|
default: ''
|
},
|
// 默认选中的下标
|
defaultIndex: {
|
type: [String, Number],
|
default: ''
|
},
|
// 列表高度,若不设置会展示所有列表选项
|
listHeight: {
|
type: String,
|
default: ''
|
},
|
|
},
|
data() {
|
return {
|
selectItem: '', // 选中的值
|
selectItemIdx: null, // 选中的下标
|
showOptionList: false, // 显示下拉
|
}
|
},
|
computed: {
|
value() {
|
if (!this.selectItem && this.defaultIndex !== '' && this.optionList.length) {
|
this.selectItemIdx = this.defaultIndex
|
let _val = this.rangeKey ? this.optionList[this.defaultIndex][this.rangeKey] : this.optionList[this
|
.defaultIndex]
|
this.$emit('change', this.optionList[this.defaultIndex])
|
return _val
|
} else if (this.selectItem) {
|
return this.rangeKey ? this.selectItem[this.rangeKey] : this.selectItem
|
} else {
|
return ''
|
}
|
},
|
ListHeightVal() {
|
if (this.showOptionList) {
|
if (this.listHeight) {
|
return this.listHeight
|
} else {
|
// 70是单行高度,24是列表上下内边距
|
return (this.optionList.length * 70 + 24) + 'rpx'
|
}
|
} else {
|
return '0'
|
}
|
|
}
|
},
|
// #ifdef H5
|
mounted() {
|
document.addEventListener('click', this.handleDocumentClick);
|
},
|
beforeDestroy() {
|
document.removeEventListener('click', this.handleDocumentClick);
|
},
|
// #endif
|
methods: {
|
// 点击组件外部区域时自动收起菜单
|
handleDocumentClick(e) {
|
const dropdown = this.$refs.dropdown.$el;
|
if (dropdown && !dropdown.contains(e.target)) {
|
this.showOptionList = false;
|
}
|
},
|
// 控制列表显示与隐藏
|
fnShowOptionList() {
|
this.$emit('click')
|
if (this.optionList.length) {
|
this.showOptionList = !this.showOptionList
|
} else {
|
// 如果列表为空,发送一个事件
|
this.$emit('openNull')
|
console.log('mu-dropdown列表数据唯空无法展开')
|
}
|
|
},
|
// 点击列表时触发
|
fnChangeOption(item, index) {
|
this.selectItem = item
|
this.selectItemIdx = index
|
this.showOptionList = false
|
this.$emit('change', item)
|
},
|
/**
|
* @description 样式转换
|
* 对象转字符串,或者字符串转对象
|
* @param {object | string} customStyle 需要转换的目标
|
* @param {String} target 转换的目的,object-转为对象,string-转为字符串
|
* @returns {object|string}
|
*/
|
addStyle(customStyle, target = 'object') {
|
// 字符串转字符串,对象转对象情形,直接返回
|
if (this.empty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target ===
|
'string' &&
|
typeof(customStyle) === 'string') {
|
return customStyle
|
}
|
// 字符串转对象
|
if (target === 'object') {
|
// 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的
|
customStyle = this.trim(customStyle)
|
// 根据";"将字符串转为数组形式
|
const styleArray = customStyle.split(';')
|
const style = {}
|
// 历遍数组,拼接成对象
|
for (let i = 0; i < styleArray.length; i++) {
|
// 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤
|
if (styleArray[i]) {
|
const item = styleArray[i].split(':')
|
style[this.trim(item[0])] = this.trim(item[1])
|
}
|
}
|
return style
|
}
|
// 这里为对象转字符串形式
|
let string = ''
|
for (const i in customStyle) {
|
// 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名
|
const key = i.replace(/([A-Z])/g, '-$1').toLowerCase()
|
string += `${key}:${customStyle[i]};`
|
}
|
// 去除两端空格
|
return this.trim(string)
|
},
|
|
/**
|
* @description 去除空格
|
* @param String str 需要去除空格的字符串
|
* @param String pos both(左右)|left|right|all 默认both
|
*/
|
trim(str, pos = 'both') {
|
str = String(str)
|
if (pos == 'both') {
|
return str.replace(/^\s+|\s+$/g, '')
|
}
|
if (pos == 'left') {
|
return str.replace(/^\s*/, '')
|
}
|
if (pos == 'right') {
|
return str.replace(/(\s*$)/g, '')
|
}
|
if (pos == 'all') {
|
return str.replace(/\s+/g, '')
|
}
|
return str
|
},
|
/**
|
* 判断是否为空
|
*/
|
empty(value) {
|
switch (typeof value) {
|
case 'undefined':
|
return true
|
case 'string':
|
if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true
|
break
|
case 'boolean':
|
if (!value) return true
|
break
|
case 'number':
|
if (value === 0 || isNaN(value)) return true
|
break
|
case 'object':
|
if (value === null || value.length === 0) return true
|
for (const i in value) {
|
return false
|
}
|
return true
|
}
|
return false
|
}
|
|
}
|
}
|
</script>
|
|
<style lang="scss">
|
// 去掉列表滚动条
|
::-webkit-scrollbar {
|
display: none;
|
}
|
|
.mask {
|
width: 100vw;
|
height: 100vh;
|
position: fixed;
|
position: relative;
|
background: #A3A3A3;
|
opacity: .5;
|
z-index: 9;
|
}
|
|
.option {
|
width: 100%;
|
height: 100%;
|
position: relative;
|
|
.option-select-title {
|
height: 100%;
|
padding: 0 20rpx;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
|
.inp-select {
|
width: 100%;
|
height: 100%;
|
}
|
|
.trans {
|
transition: transform 0.2s;
|
}
|
|
.trans-from {
|
transform: rotate(-180deg);
|
}
|
|
}
|
|
.option-list {
|
box-sizing: content-box;
|
width: calc(100% - 20rpx);
|
// height: 0;
|
border-radius: 25rpx;
|
background: #FFF;
|
overflow: scroll;
|
position: absolute;
|
top: calc(100% + 20rpx);
|
left: 10rpx;
|
right: 10rpx;
|
z-index: 10;
|
box-shadow: 0 0 15rpx #A3A3A3;
|
transition: height .2s;
|
|
.option-list-padding {
|
padding: 12rpx 0;
|
}
|
|
.option-item {
|
height: 70rpx;
|
text-align: center;
|
line-height: 70rpx;
|
}
|
|
}
|
|
|
}
|
</style>
|