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.
 
 
 
 

324 lines
7.8 KiB

<template>
<view class="select-container" v-show="show" @touchmove.stop.prevent>
<view class="mask" :class="activeClass ? 'mask-show' : ''" @tap="onCancel(true)"></view>
<view class="select-box" :class="activeClass ? 'select-box-show' : ''">
<view class="header">
<text class="cancel" @tap="onCancel">{{cancelText}}</text>
<view class="all" @tap="onAllToggle" v-if="allShow">
<text :class="isAll ? 'all-active' : ''">全选</text>
</view>
<text class="confirm" @tap="onConfirm">{{confirmText}}</text>
</view>
<view class="body-warp">
<scroll-view class="body" scroll-y="true">
<slot v-if="!data.length" name="tips">
<view class="empty-tips">暂无数据~</view>
</slot>
<view
class="select-item"
:class="[item.disabled ? 'disabled' : '',selectedArr[index] ? 'selected' : '']"
v-for="(item,index) in data"
:key="item[valueName]"
@tap="onSelected(index)"
>
<view class="label">{{item[labelName]}}</view>
<text v-show="selectedArr[index]" class="selected-icon"></text>
</view>
</scroll-view>
</view>
</view>
</view>
</template>
<!-- 多选组件 -->
<script>
export default {
model: {
prop: "value",
event: ["input"]
},
data() {
return {
show: false, //是否显示
activeClass: false, //激活样式状态
selectedArr: [], //选择对照列表
selectedArrOld: [] //选择对照列表上一次的数据
};
},
onShow() {
this.show = this.value;
},
computed: {
// 返回是否全选
isAll() {
let wipeDisabledList = this.returnWipeDisabledList();
if (!wipeDisabledList.length) return false;
return !wipeDisabledList.includes(false);
}
},
props: {
// 双向绑定--是否调起底部
value: {
type: Boolean,
default: false
},
// 取消按钮文字
cancelText: {
type: String,
default: "取消"
},
// 确认按钮文字
confirmText: {
type: String,
default: "确认"
},
// label对应的key名称
labelName: {
type: String,
default: "label"
},
// value对应的key名称
valueName: {
type: String,
default: "value"
},
// 是否允许点击遮罩层关闭
maskCloseAble: {
type: Boolean,
default: true
},
// 是否显示全选
allShow: {
type: Boolean,
default: true
},
// 模式
mode: {
type: String,
default: "multiple"
},
// 默认选中值
defaultSelected: {
type: Array,
default: function() {
return [];
}
},
// 数据源
data: {
type: Array,
required: true,
default: () => {
return [];
}
}
},
watch: {
async value(newVal) {
this.show = newVal;
await this.$nextTick();
this.activeClass = newVal;
if (newVal) {
this.selectedArrOld = JSON.parse(JSON.stringify(this.selectedArr));
}
},
show(newVal) {
this.$emit("input", newVal);
this.$emit("change", newVal);
},
data: {
// 设置初始选择对照列表
handler(list) {
this.selectedArr = list.map(el => false);
this.setItemActiveState();
},
deep: true,
immediate: true
},
defaultSelected: {
handler() {
this.setItemActiveState();
},
deep: true,
immediate: true
}
},
methods: {
// 设置默认选中通用办法
setItemActiveState() {
if (this.data.length && this.defaultSelected.length) {
this.data.forEach((item, i) => {
for (let n = 0; n < this.defaultSelected.length; n++) {
if (
!item.disabled &&
item[this.valueName] === this.defaultSelected[n]
) {
this.selectedArr.splice(i, 1, true);
break;
}
}
});
}
},
/**
* 选择事件
* @index {Number} 点击下标
*/
onSelected(index) {
if (this.data[index].disabled) return;
let index2Active = this.selectedArr[index];
this.selectedArr.splice(index, 1, !index2Active);
},
// 取消事件
onCancel(isMask) {
if (!isMask || this.maskCloseAble) {
this.show = false;
this.selectedArr = JSON.parse(JSON.stringify(this.selectedArrOld));
} else {
return;
}
this.$emit("cancel");
},
// 返回去除了disabled状态后的对照列表
returnWipeDisabledList() {
let arr = [];
this.selectedArr.forEach((el, index) => {
if (!this.data[index].disabled) arr.push(el);
});
return arr;
},
// 全选/非全选事件
onAllToggle() {
let wipeDisabledList = this.returnWipeDisabledList();
// 如果去除了disabled的对照列表有false的数据,代表未全选
if (wipeDisabledList.includes(false)) {
this.selectedArr.forEach((el, index) => {
if (!this.data[index].disabled)
this.selectedArr.splice(index, 1, true);
});
} else {
this.selectedArr.forEach((el, index) => {
if (!this.data[index].disabled)
el = this.selectedArr.splice(index, 1, false);
});
}
},
// 确定事件
onConfirm() {
this.show = false;
let selectedData = [];
this.selectedArr.forEach((el, index) => {
if (el) {
selectedData.push(this.data[index]);
}
});
if (this.mode === "multiple") {
this.$emit("confirm", selectedData);
} else {
let backData = selectedData[0] || {};
this.$emit("confirm", backData);
}
}
}
};
</script>
<style lang="scss" scoped>
.select-container {
width: 100vw;
height: 100vh;
position: fixed;
left: 0;
top: 0;
z-index: 999;
$paddingLR: 18rpx;
.mask {
width: 100%;
height: 100%;
background-color: $uni-bg-color-mask;
opacity: 0;
transition: opacity 0.3s;
&.mask-show {
opacity: 1;
}
}
// 选择器内容区域
.select-box {
width: 100%;
position: absolute;
bottom: 0;
left: 0;
transform: translate3d(0px, 100%, 0px);
background-color: $uni-bg-color;
transition: all 0.3s;
&.select-box-show {
transform: translateZ(0);
}
.header {
display: flex;
box-sizing: border-box;
width: 100%;
justify-content: space-between;
border-bottom: 1px solid $uni-border-color;
line-height: 76rpx;
font-size: 30rpx;
padding: 0 $paddingLR;
.cancel {
color: $uni-text-color-grey;
}
.all {
color: $uni-color-success;
.all-active {
&::after {
display: inline-block;
content: "✔";
padding-left: 8rpx;
}
}
}
.confirm {
color: $uni-color-primary;
}
}
.body-warp {
width: 100%;
height: 30vh;
box-sizing: border-box;
padding: 20rpx $paddingLR;
}
.body {
width: 100%;
height: 100%;
overflow-y: auto;
.empty-tips {
margin-top: 25%;
text-align: center;
font-size: 26rpx;
color: $uni-color-error;
}
.select-item {
display: flex;
font-size: 26rpx;
line-height: 58rpx;
color: #303133;
position: relative;
transition: all 0.3s;
&.selected {
color: $uni-color-primary;
}
&.disabled {
color: $uni-text-color-disable;
}
> .label {
flex: 1;
text-align: center;
}
> .selected-icon {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
}
}
}
}
}
</style>