9 changed files with 557 additions and 62 deletions
@ -0,0 +1,70 @@ |
|||||
|
#### props |
||||
|
|
||||
|
|名称 |类型 |默认值 |说明| |
||||
|
|- |- |- |-| |
||||
|
|width |string |'300rpx' |选择框宽度| |
||||
|
|height |string |'60rpx' |选择框高度| |
||||
|
|round |boolean |true |是否开启圆角| |
||||
|
|bgColor|string |'#fff' |选择框的背景颜色| |
||||
|
|color |string |'#606266' |字体颜色| |
||||
|
|placeholderColor|string |'color:#bcbec4' |提示文字的字体颜色| |
||||
|
|defaultValue |string |'请选择' |默认显示的名称 | | |
||||
|
|valueName|string |'value' |显示的内容字段名,必传| |
||||
|
|list |array |[] |展示的内容列表| |
||||
|
|showClose|boolean |true |是否显示删除按钮| |
||||
|
|multiple|boolean |false |是否开启多选| |
||||
|
|filterable|boolean |false |是否开启搜索功能,开启后直接输入值不选择也可以保存内容| |
||||
|
|
||||
|
|
||||
|
#### events |
||||
|
|
||||
|
|事件名|说明| |
||||
|
|-|-| |
||||
|
|change|选择的内容改变时触发,返回的参数为列表的item| |
||||
|
|
||||
|
|
||||
|
#### 使用举例 |
||||
|
|
||||
|
需要绑定的值通过`v-model`绑定,如下面的`chooseValue`,需要获取item的值可以监听`@change`事件 |
||||
|
|
||||
|
开启多选`multiple`时,双向绑定的值为数组类型,在change事件中根据自己需求进行处理。 |
||||
|
|
||||
|
`valueName`属性必须传,告知组件需要显示的是什么值 |
||||
|
|
||||
|
```html |
||||
|
<template> |
||||
|
<view class="login"> |
||||
|
<w-select style="margin-left: 20rpx;" |
||||
|
v-model='chooseValue' |
||||
|
defaultValue="所有审批人" |
||||
|
:list='list' |
||||
|
valueName='content' |
||||
|
@change='change'> |
||||
|
</w-select> |
||||
|
</view> |
||||
|
</template> |
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
chooseValue: "", |
||||
|
list: [{ |
||||
|
id: 1, |
||||
|
content: '张三' |
||||
|
}, { |
||||
|
id: 2, |
||||
|
content: '李四' |
||||
|
}, { |
||||
|
id: 3, |
||||
|
content: '王五' |
||||
|
}], |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
change(e) { |
||||
|
console.log('chooseValue', this.chooseValue) |
||||
|
} |
||||
|
}, |
||||
|
} |
||||
|
</script> |
||||
|
``` |
@ -0,0 +1,395 @@ |
|||||
|
<template> |
||||
|
<view class="wSelect" :class="show ? 'active' : ''" :style="{ |
||||
|
width: width, |
||||
|
height: height, |
||||
|
backgroundColor: bgColor, |
||||
|
borderRadius: round ? '6rpx' : 'none' |
||||
|
}"> |
||||
|
<view @click="openSelect" class="pickerSelect"> |
||||
|
<view :style="{ height: height }" v-if="multiple" class="multipleChoice"> |
||||
|
<view class="defaultValue" :style="{ color: color }" v-if="!inputData && multiSelectList.length < 1"> |
||||
|
{{ defaultValue }} |
||||
|
</view> |
||||
|
<view class="option" v-if="multiSelectList.length > 0"> |
||||
|
<view class="">{{ multiSelectList[0][valueName] }}</view> |
||||
|
<image class="img" @click.stop="multiDelete" :src="refreshUrl" mode=""></image> |
||||
|
</view> |
||||
|
<view v-if="multiSelectList.length > 1" class="more">{{ multiLength }}</view> |
||||
|
<input :disabled="!filterable" |
||||
|
:style="{ height: height, paddingRight: '30rpx', fontSize: '28rpx', color: color }" |
||||
|
@input="inputChange" type="text" v-model="inputData" /> |
||||
|
</view> |
||||
|
<input v-else :disabled="!filterable" |
||||
|
:style="{ height: height, paddingRight: '30rpx', fontSize: '30rpx', color:'#000' }" @input="inputChange" |
||||
|
:placeholder="defaultValue" :placeholder-style="placeholderColor" type="text" v-model="inputData" /> |
||||
|
<view v-if="!inputData || !showClose" :class="show ? 'arrow-up' : 'arrow'"></view> |
||||
|
<view class="showClose" v-if="showClose && inputData"> |
||||
|
<image @click.stop="refreshValue" :src="refreshUrl" mode=""></image> |
||||
|
</view> |
||||
|
<view v-show="show" :class="isShow ? 'animation-top' : 'animation-bottom'" class="tips" |
||||
|
:style="{ top: tipsPositon }"></view> |
||||
|
</view> |
||||
|
<!-- 单选时的下拉框 --> |
||||
|
<view v-if="!multiple" :style="{ width: '75%', color: '#737478' }" v-show="show" |
||||
|
:class="isShow ? 'animation-top' : 'animation-bottom'" class="content"> |
||||
|
<view class="item" :class="value == item[valueName] ? 'choose' : ''" v-for="(item, index) in listData" |
||||
|
:key="index" @click="clickSelect(item)"> |
||||
|
{{ item[valueName] }} |
||||
|
</view> |
||||
|
<view class="item" v-if="listData.length < 1">无匹配项</view> |
||||
|
</view> |
||||
|
<!-- 多选的下拉框 --> |
||||
|
<view v-else :style="{ width: width }" v-show="show" :class="isShow ? 'animation-top' : 'animation-bottom'" |
||||
|
class="content"> |
||||
|
<view class="item" :class="multiSelectList.find(res => res[valueName] == item[valueName]) ? 'choose' : ''" |
||||
|
v-for="(item, index) in listData" :key="index" @click="multiSelect(item)"> |
||||
|
{{ item[valueName] }} |
||||
|
</view> |
||||
|
<view class="item" v-if="listData.length < 1">无匹配项</view> |
||||
|
</view> |
||||
|
<view v-if="show" @click="closeContentSelect" class="contentMask"></view> |
||||
|
</view> |
||||
|
</template> |
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'wSelect', |
||||
|
props: { |
||||
|
//是否多选 |
||||
|
multiple: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
//是否可搜索 |
||||
|
filterable: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
//是否显示关闭按钮 |
||||
|
showClose: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
width: { |
||||
|
type: String, |
||||
|
default: '300rpx' |
||||
|
}, |
||||
|
color: { |
||||
|
type: String, |
||||
|
default: '#606266' |
||||
|
}, |
||||
|
placeholderColor: { |
||||
|
type: String, |
||||
|
default: 'color:#bcbec4' |
||||
|
}, |
||||
|
bgColor: { |
||||
|
type: String, |
||||
|
default: '#fff' |
||||
|
}, |
||||
|
height: { |
||||
|
type: String, |
||||
|
default: '60rpx' |
||||
|
}, |
||||
|
//默认显示的内容 |
||||
|
defaultValue: { |
||||
|
type: String, |
||||
|
default: '请选择' |
||||
|
}, |
||||
|
//显示的内容 |
||||
|
valueName: { |
||||
|
type: String, |
||||
|
required: true |
||||
|
}, |
||||
|
list: { |
||||
|
type: Array, |
||||
|
default: () => [], |
||||
|
required: true |
||||
|
}, |
||||
|
//双向绑定的值 |
||||
|
value: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
round: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
value: { |
||||
|
immediate: true, |
||||
|
handler(newValueData) { |
||||
|
if (!this.inputData) { |
||||
|
this.inputData = newValueData |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
show: false, |
||||
|
isShow: false, |
||||
|
multiSelectList: [], |
||||
|
inputData: '', |
||||
|
listData: this.list, |
||||
|
refreshUrl: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDQ4IDQ4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgZmlsbD0id2hpdGUiIGZpbGwtb3BhY2l0eT0iMC4wMSIvPjxwYXRoIGQ9Ik0yNCA0NEMzNS4wNDU3IDQ0IDQ0IDM1LjA0NTcgNDQgMjRDNDQgMTIuOTU0MyAzNS4wNDU3IDQgMjQgNEMxMi45NTQzIDQgNCAxMi45NTQzIDQgMjRDNCAzNS4wNDU3IDEyLjk1NDMgNDQgMjQgNDRaIiBmaWxsPSJub25lIiBzdHJva2U9IiM3YzZlNmUiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0yOS42NTY5IDE4LjM0MzFMMTguMzQzMiAyOS42NTY4IiBzdHJva2U9IiM3YzZlNmUiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTE4LjM0MzIgMTguMzQzMUwyOS42NTY5IDI5LjY1NjgiIHN0cm9rZT0iIzdjNmU2ZSIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4=' |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
multiLength() { |
||||
|
const length = this.multiSelectList.length - 1 |
||||
|
return '+' + length |
||||
|
}, |
||||
|
tipsPositon() { |
||||
|
let num = this.height.replace('rpx', '') |
||||
|
let numAdd = Number(num) + Number(10) |
||||
|
return numAdd + 'rpx' |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
openSelect() { |
||||
|
this.listData = this.list |
||||
|
console.log("listData>>>", this.listData) |
||||
|
if (this.show === true) { |
||||
|
this.isShow = false |
||||
|
} else { |
||||
|
this.isShow = true |
||||
|
} |
||||
|
setTimeout(() => { |
||||
|
this.show = !this.show |
||||
|
}, 200) |
||||
|
}, |
||||
|
clickSelect(item) { |
||||
|
this.$emit('input', item[this.valueName]) |
||||
|
this.inputData = item[this.valueName] |
||||
|
this.$emit('change', item) |
||||
|
this.show = false |
||||
|
}, |
||||
|
inputChange(e) { |
||||
|
console.log(e.detail.value) |
||||
|
let value = e.detail.value |
||||
|
this.$emit('input', value) |
||||
|
if (value) { |
||||
|
this.listData = this.listData.filter(item => item[this.valueName].includes(value)) |
||||
|
} else { |
||||
|
this.listData = this.list |
||||
|
} |
||||
|
}, |
||||
|
multiSelect(item) { |
||||
|
let index = this.multiSelectList.findIndex(res => res[this.valueName] == item[this.valueName]) |
||||
|
if (index > -1) { |
||||
|
this.multiSelectList.splice(index, 1) |
||||
|
} else { |
||||
|
this.multiSelectList.push(item) |
||||
|
} |
||||
|
this.inputData = '' |
||||
|
this.listData = this.list |
||||
|
this.$emit('input', this.multiSelectList) |
||||
|
this.$emit('change', item) |
||||
|
}, |
||||
|
refreshValue() { |
||||
|
this.$emit('input', '') |
||||
|
this.inputData = '' |
||||
|
this.listData = this.list |
||||
|
this.$emit('change', '') |
||||
|
this.show = false |
||||
|
}, |
||||
|
multiDelete() { |
||||
|
this.multiSelectList.splice(0, 1) |
||||
|
this.$emit('input', this.multiSelectList) |
||||
|
if (this.multiSelectList.length < 1) { |
||||
|
this.show = false |
||||
|
} |
||||
|
}, |
||||
|
closeContentSelect() { |
||||
|
this.isShow = false |
||||
|
setTimeout(() => { |
||||
|
this.show = false |
||||
|
}, 200) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
<style lang="scss" scoped> |
||||
|
.wSelect { |
||||
|
// border: 2rpx solid #dcdfe6; |
||||
|
transition: all 0.5s; |
||||
|
|
||||
|
.pickerSelect { |
||||
|
border-radius: 6rpx; |
||||
|
padding: 0 20rpx; |
||||
|
position: relative; |
||||
|
transition: all 0.6s; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
input { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.tips { |
||||
|
position: absolute; |
||||
|
margin-left: 30rpx; |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
border-bottom: 12rpx solid #fff; |
||||
|
border-left: 12rpx solid transparent; |
||||
|
border-right: 12rpx solid transparent; |
||||
|
z-index: 1000; |
||||
|
} |
||||
|
|
||||
|
.showClose { |
||||
|
width: 30rpx; |
||||
|
height: 30rpx; |
||||
|
position: absolute; |
||||
|
right: 14rpx; |
||||
|
top: 50%; |
||||
|
transform: translateY(-50%); |
||||
|
|
||||
|
image { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.arrow { |
||||
|
transition: all 0.3s; |
||||
|
position: absolute; |
||||
|
top: 44%; |
||||
|
right: 20rpx; |
||||
|
border-left: 2rpx solid #999999; |
||||
|
border-bottom: 2rpx solid #999999; |
||||
|
height: 16rpx; |
||||
|
width: 16rpx; |
||||
|
transform: translateY(-50%) rotate(-45deg); |
||||
|
-webkit-transform: translateY(-50%) rotate(-45deg); |
||||
|
border-right: 2rpx solid transparent; |
||||
|
border-top: 2rpx solid transparent; |
||||
|
display: inline-block; |
||||
|
} |
||||
|
|
||||
|
.arrow-up { |
||||
|
@extend .arrow; |
||||
|
transform: rotate(-235deg); |
||||
|
} |
||||
|
|
||||
|
.multipleChoice { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
input { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.defaultValue { |
||||
|
position: absolute; |
||||
|
left: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.option { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
background-color: #f4f4f5; |
||||
|
border-radius: 10rpx; |
||||
|
font-size: 24rpx; |
||||
|
color: #aa93b1; |
||||
|
padding: 6rpx 12rpx; |
||||
|
|
||||
|
.img { |
||||
|
margin-left: 4rpx; |
||||
|
width: 30rpx; |
||||
|
height: 30rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.more { |
||||
|
background-color: #f4f4f5; |
||||
|
border-radius: 10rpx; |
||||
|
font-size: 24rpx; |
||||
|
color: #aa93b1; |
||||
|
margin-left: 10rpx; |
||||
|
padding: 6rpx 12rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
position: absolute; |
||||
|
margin-top: 20rpx; |
||||
|
padding: 20rpx; |
||||
|
border-radius: 4px; |
||||
|
background-color: #fff; |
||||
|
box-shadow: rgba(0, 0, 0, 0.5) 0px 3px 8px; |
||||
|
z-index: 999; |
||||
|
max-height: 300rpx; |
||||
|
overflow-y: auto; |
||||
|
|
||||
|
.item { |
||||
|
padding: 10rpx; |
||||
|
height: 60rpx; |
||||
|
line-height: 40rpx; |
||||
|
margin-bottom: 10rpx; |
||||
|
font-size: 28px; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
.choose { |
||||
|
background-color: #f5f7fa; |
||||
|
color: #409eff; |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.animation-top { |
||||
|
animation-name: slide-top; |
||||
|
animation-duration: 0.5s; |
||||
|
animation-timing-function: ease-out; |
||||
|
animation-fill-mode: both; |
||||
|
} |
||||
|
|
||||
|
.animation-bottom { |
||||
|
animation-name: slide-bottom; |
||||
|
animation-duration: 0.2s; |
||||
|
animation-timing-function: ease-out; |
||||
|
animation-fill-mode: both; |
||||
|
} |
||||
|
|
||||
|
@keyframes slide-top { |
||||
|
0% { |
||||
|
opacity: 0; |
||||
|
transform: translateY(-20%); |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
opacity: 1; |
||||
|
transform: translateY(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes slide-bottom { |
||||
|
0% { |
||||
|
opacity: 1; |
||||
|
transform: translateY(0); |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
opacity: 0; |
||||
|
transform: translateY(-20%); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.active { |
||||
|
// border: 2rpx solid #409eff; |
||||
|
} |
||||
|
|
||||
|
.contentMask { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
right: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
z-index: 998; |
||||
|
} |
||||
|
</style> |
@ -1,17 +1,17 @@ |
|||||
{ |
{ |
||||
"name": "uniapp", |
"id": "w-select", |
||||
"version": "1.0.0", |
"displayName": "下拉选择框w-select", |
||||
"description": "宇运动商城", |
"version": "1.1.3", |
||||
"main": "main.js", |
"description": "用于下拉选择,使用v-model双向绑定值", |
||||
"dependencies": { |
"keywords": [ |
||||
"amap-js": "^1.2.1", |
"下拉选择框", |
||||
"jweixin-module": "^1.6.0", |
"单选框", |
||||
"weixin-js-sdk": "^1.4.0-test" |
"vue2" |
||||
}, |
], |
||||
"devDependencies": {}, |
"dcloudext": { |
||||
"scripts": { |
"category": [ |
||||
"test": "echo \"Error: no test specified\" && exit 1" |
"前端组件", |
||||
}, |
"通用组件" |
||||
"author": "", |
] |
||||
"license": "ISC" |
} |
||||
} |
} |
Loading…
Reference in new issue