修改后的选择器说明:
1、支持可限制多选。第二次点击同一人清空当前选择,多选上限后先清空才能改选他人。
2、可限制单选。点击一个人员后,非必须清空选择,点击第二个人员自动切换。
3、选择后的人员不再显示在输入框内,而是显示在input下方区域,删改和与下拉框联动功能保留。
4、因为业务需求为:选择后的人员显示在input下方而非input框内,选择所有人员后,点击下拉框以外的位置时输入框做了清空,所以必填验证目前不太友好,需要搭配css和<uni-easyinput/>标签,然后将该标签隐藏只保留验证规则,搭配代码在最底部【6、单选时父页面使用】。
5、插件内部分方法和props值没有使用到,没有做代码修改。
<!--多项选择器 -->
<template>
<view class="uni-select-cy" :style="{ 'z-index': zindex }">
<!-- 搜索 -->
<uni-easyinput :placeholder="placeholder" @input="inputFun" @clear="clearFun" v-model="searchText"></uni-easyinput>
<!-- 下拉选项 -->
<scroll-view class="uni-select-cy-options" :scroll-y="true" v-show="active" @scrolltolower="scrolltolower"
ref="optionsRef" :style="style">
<template v-if="options.length !== 0">
<view class="uni-select-cy-item" :class="{ active: realValue.includes(item[svalue]) }"
v-for="(item, index) in options" :key="index" @click.stop="handleChange(index, item)">
{{ `(${item[svalue]}) ${item[slabel]}` }}
</view>
</template>
<template v-else>
<view class="none">暂无数据</view>
</template>
</scroll-view>
<!-- 已选择数据显示 -->
<scroll-view :style="style" style="position: absolute;" class="chooseCss" :scroll-y="true">
<view v-for="(item, index) in realValue" :key="index" class="ListAll">
<view>{{ `(${item}) ${includesList(item)}` }}</view>
<view class="iconList" @click.stop="handleRemove(index)"><u-icon name="close" labelColor='#F94848'
labelSize="14px" color="#F94848" size="20"></u-icon></view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
props: {
// 单选 radio /多选 multiple
selectNumber: {
type: String,
default: "radio"
},
//是否显示全部清空按钮
showClearIcon: {
type: Boolean,
default: false
},
//是否显示单个删除
showValueClear: {
type: Boolean,
default: true
},
// 置于顶层
zindex: {
type: Number,
default: 999
},
//禁用组件
disabled: {
type: Boolean,
default: false
},
// 下拉数据
options: {
type: Array,
default() {
return [];
}
},
// 插件自带内容,暂不明确作用
value: {
type: Array,
default() {
return [];
}
},
// 默认文字
placeholder: {
type: String,
default: '请选择'
},
// 自定义lable --name
slabel: {
type: String,
default: 'nickName',
},
// 自定义value --code
svalue: {
type: String,
default: 'userName',
},
// 是否开启分页,插件自带内容保存
isPaging: {
type: Boolean,
default: false
}
},
data() {
return {
active: false, //组件是否激活
changevalue: [], //搜索框同步
realValue: [], //已选中的数据
// 下拉区动态框
style: {
height: '132px'
},
// 搜索值
searchText: "",
// 选中的数据对象
realValueOne: {},
};
},
watch: {
// 组件自带页面数据初始化,不删除处理
value() {
//初始化
this.init();
},
// 监听值得变化
options(newVal, oldVal) {
this.active = true
this.style.height = newVal.length >= 4 ? '132px' : newVal.length === 0 ? '72px' : (newVal.length * 30 + 12) + 'px'
}
},
mounted() {
// 除下拉区外点击任意地方关闭下拉框
if (window.history && window.history.pushState) {
history.pushState(null, null, document.URL) // 这里有没有都无所谓,最好是有以防万一
window.addEventListener('popstate', this.goBack, false) // 回退时执行goback方法
}
document.addEventListener('click', this.external)// 全局点击事件
},
// 销毁钩子
beforeUnmount() {
// 清除全局事件
document.removeEventListener('click', this.external)
},
methods: {
// 输入关键字搜索人员列表
inputFun(e) {
this.searchText = e
this.$emit('searchFun', { searchText: e })
},
// 移除焦点关闭弹出层
external() {
if (this.$refs.optionsRef && this.active) {
this.active = false
this.searchText = ''
}
// 传值给父组件
this.$emit('selectOptionFun', { realValueOne: this.realValueOne, realValue: this.realValue })
},
// 清除搜索数据关闭选项卡
clearFun() {
this.searchText = ''
self.active = false
// 传值给父组件
this.$emit('selectOptionFun', { realValueOne: this.realValueOne, realValue: this.realValue })
},
// 插件自带初始化内容
init() {
if (this.value.length > 0) {
this.changevalue = this.options.map(item => {
this.value.forEach(i => {
if (item[this.svalue] === i[this.svalue]) {
return item;
}
});
});
this.realValue = this.value;
} else {
this.changevalue = [];
this.realValue = [];
}
},
// 分页
scrolltolower() {
if (this.isPaging) {
this.$emit('scrolltolower')
}
},
//点击展示选项
handleSelect() {
if (this.disabled) return;
this.active = !this.active;
},
//移除数据
handleRemove(index) {
this.searchText = ''
if (index === null) {
this.realValue = [];
this.changevalue = [];
} else {
this.realValue.splice(index, 1);
this.changevalue.splice(index, 1);
}
// 单条数据时选中数据为空
this.realValueOne = {}
// 传值给父组件
this.$emit('selectOptionFun', { realValueOne: this.realValueOne, realValue: this.realValue })
},
//点击组件列
handleChange(index, item) {
// 当组件多选时,保持原有逻辑
if (this.selectNumber === "multiple") {
let arrIndex = this.realValue.indexOf(item[this.svalue]);
if (arrIndex > -1) {
this.changevalue.splice(arrIndex, 1);
this.realValue.splice(arrIndex, 1);
} else {
this.realValueOne = item
this.changevalue.push(item);
this.realValue.push(item[this.svalue]);
}
// 当组件单选时,支持选中其它内容自动切换颜色和数据
} else if (this.selectNumber === "radio") {
let arrIndex = this.realValue.indexOf(item[this.svalue]);
this.realValueOne = item
this.changevalue.push(item);
this.realValue.push(item[this.svalue]);
// 当长度=1时,切换选中项
if (this.changevalue.length = 1) {
this.changevalue.splice(0);
this.realValue.splice(0);
this.changevalue.push(item);
this.realValue.push(item[this.svalue]);
}
// 点击自己时取消选中
if (arrIndex > -1) {
this.changevalue.splice(0);
this.realValue.splice(0);
}
}
// 之前的逻辑报错,以备之后直接用
// let arrIndex = this.realValue.indexOf(item[this.svalue]);
// console.log(arrIndex, item[this.svalue], this.realValue)
// if (arrIndex > -1) {
// this.changevalue.splice(arrIndex, 1);
// this.realValue.splice(arrIndex, 1);
// } else {
// if (this.selectNumber === "radio" && this.realValue.length === 1) {
// return
// }
// this.realValueOne = item
// this.changevalue.push(item);
// this.realValue.push(item[this.svalue]);
// }
this.$emit('change', this.changevalue, this.realValue);
},
// 显示选中人员姓名
includesList(item) {
let nickNameOne = ''
this.changevalue.forEach(i => {
if (i.userName === item) {
nickNameOne = i.nickName
}
})
return nickNameOne
},
// 返回tabbar
goBack() {
this.$tab.switchTab('/pages/task/index')
}
}
};
</script>
<style lang="scss" scoped>
.input {
color: #303133;
font-family: '微软雅黑 Bold', '微软雅黑 Regular', '微软雅黑', sans-serif;
width: 100%;
display: inline-block;
}
::v-deep .uni-input-input {
font-size: 15px;
}
.none {
text-align: center;
line-height: 50px;
color: #303133;
font-family: '微软雅黑 Bold', '微软雅黑 Regular', '微软雅黑', sans-serif;
}
.chooseCss {
padding: 10px 5px;
margin-top: 10px;
}
.iconList {
width: 10px;
height: 10px;
color: rgb(249, 72, 72);
margin-left: 10px;
}
.ListAll {
display: flex;
flex-direction: row;
align-items: center;
line-height: 25px;
font-size: 15px;
font-family: '微软雅黑 Bold', '微软雅黑 Regular', '微软雅黑', sans-serif;
color: #303133;
}
.uni-select-cy {
position: relative;
z-index: 999;
.uni-select-mask {
width: 100%;
height: 100%;
}
/* 删除按钮样式*/
.close-icon {
height: 100%;
width: 20px;
display: flex;
align-items: center;
justify-content: center;
z-index: 3;
cursor: pointer;
text {
position: relative;
background: #c0c4cc;
width: 13px;
height: 13px;
border-radius: 50%;
border: 1px solid #bbb;
&::before,
&::after {
content: '';
position: absolute;
left: 20%;
top: 50%;
height: 1px;
width: 60%;
transform: rotate(45deg);
background-color: #909399;
}
&::after {
transform: rotate(-45deg);
}
}
}
//所有情空的定位
.close-postion {
position: absolute;
right: 35px;
top: 0;
height: 100%;
width: 15px;
}
/* 多选盒子 */
.uni-select-multiple {
overflow-x: auto;
display: flex;
.uni-select-multiple-item {
background: #f4f4f5;
margin-right: 5px;
padding: 2px 4px;
border-radius: 4px;
color: #909399;
display: flex;
}
}
// select部分
.uni-select-cy-select {
user-select: none;
position: relative;
z-index: 3;
height: 36px;
padding: 0 30px 0 10px;
box-sizing: border-box;
border-radius: 4px;
border: 1px solid rgb(229, 229, 229);
display: flex;
align-items: center;
font-size: 14px;
color: #999;
.uni-disabled {
position: absolute;
left: 0;
width: 100%;
height: 100%;
z-index: 19;
cursor: no-drop;
background: rgba(255, 255, 255, 0.5);
}
.uni-select-cy-input {
font-size: 14px;
color: #999;
display: block;
width: 96%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 30px;
box-sizing: border-box;
&.active {
color: #333;
}
}
.uni-select-cy-icon {
cursor: pointer;
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 30px;
display: flex;
align-items: center;
justify-content: center;
&::before {
content: '';
width: 1px;
height: 100%;
position: absolute;
left: 0;
top: 0;
background-color: #e5e5e5;
}
text {
display: block;
width: 0;
height: 0;
border-width: 12rpx 12rpx 0;
border-style: solid;
border-color: #bbb transparent transparent;
transition: 0.3s;
}
&.disabled {
cursor: no-drop;
text {
width: 20rpx;
height: 20rpx;
border: 2px solid #ff0000;
border-radius: 50%;
transition: 0.3s;
position: relative;
z-index: 999;
&::after {
content: '';
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 2px;
margin-top: -1px;
background-color: #ff0000;
transform: rotate(45deg);
}
}
}
}
&.active .uni-select-cy-icon {
text {
transform: rotate(180deg);
}
}
}
// options部分
.uni-select-cy-options {
user-select: none;
position: absolute;
top: calc(100% + 5px);
left: 0;
width: 100%;
height: 132px;
border-radius: 4px;
border: 1px solid rgb(229, 229, 229);
background: #fff;
padding: 5px 0;
box-sizing: border-box;
z-index: 9;
.uni-select-cy-item {
padding: 0 10px;
box-sizing: border-box;
cursor: pointer;
line-height: 30px;
transition: 0.3s;
font-family: 'PingFang SC ', 'PingFang SC', sans-serif;
color: #303133;
font-family: '微软雅黑 Bold', '微软雅黑 Regular', '微软雅黑', sans-serif;
&.active {
color: #409eff;
background-color: #f5f7fa &hover {
color: #409eff;
background-color: #f5f7fa;
}
}
&:hover {
background-color: #f5f5f5;
}
}
}
}
::v-deep uni-input-placeholder.uni-easyinput__placeholder-class {
font-size: 12px;
}
</style>
6、单选时父页面使用
//html部分,在同一个uni-forms-item中写入两个标签
<uni-easyinput v-model="str" class="noneInput"></uni-easyinput>
//select子组件
<Select/>
//js部分,rules表单校验写法同vue2
//css部分,将标签隐藏,只保留校验功能
::v-deep .noneInput.uni-easyinput {
height: 0;
margin: 0;
padding: 0;
width: 0;
.uni-easyinput__content.is-input-border {
height: 0;
margin: 0;
padding: 0;
border: 0;
.uni-easyinput__content-input {
height: 0;
margin: 0;
padding: 0;
border: 0;
.uni-input-wrapper {
height: 0;
margin: 0;
padding: 0;
border: 0;
.uni-input-placeholder,
.uni-input-input {
height: 0;
margin: 0;
padding: 0;
border: 0;
}
}
}
.uni-icons.content-clear-icon.uniui-clear {
display: none;
}
}
}