355 lines
8.4 KiB
Vue
355 lines
8.4 KiB
Vue
<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.name }}</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: true, //是否显示
|
||
activeClass: false, //激活样式状态
|
||
selectedArr: [], //选择对照列表
|
||
selectedArrOld: [], //选择对照列表上一次的数据
|
||
};
|
||
},
|
||
onShow() {
|
||
console.log(this.serviceList);
|
||
},
|
||
computed: {
|
||
// 返回是否全选
|
||
isAll() {
|
||
let wipeDisabledList = this.returnWipeDisabledList();
|
||
if (!wipeDisabledList.length) return false;
|
||
return !wipeDisabledList.includes(false);
|
||
},
|
||
},
|
||
props: {
|
||
// 双向绑定
|
||
value: {
|
||
type: Boolean,
|
||
default: true,
|
||
},
|
||
// 取消按钮文字
|
||
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 [];
|
||
},
|
||
},
|
||
},
|
||
created() {
|
||
console.log(this.data, "111111");
|
||
},
|
||
watch: {
|
||
async value(newVal) {
|
||
this.show = newVal;
|
||
await this.$nextTick();
|
||
this.activeClass = newVal;
|
||
if (newVal) {
|
||
this.selectedArrOld = JSON.parse(JSON.stringify(this.selectedArr));
|
||
}
|
||
},
|
||
async data(newVal) {
|
||
this.data = newVal;
|
||
await this.$nextTick();
|
||
console.log(this.data);
|
||
},
|
||
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.mode === "multiple"){
|
||
if (this.data[index].disabled) return;
|
||
let index2Active = this.selectedArr[index];
|
||
this.selectedArr.splice(index, 1, !index2Active);
|
||
}else{
|
||
let index0 ;
|
||
for(let i= 0;i<this.selectedArr.length;i++){
|
||
if(index==i){
|
||
let index2Active = this.selectedArr[index];
|
||
this.selectedArr.splice(index, 1, !index2Active);
|
||
}else{
|
||
this.selectedArr[i] = false;
|
||
}
|
||
}
|
||
}
|
||
// this.selectedIds.splice(i, 1, true);
|
||
},
|
||
// 取消事件
|
||
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() {
|
||
console.log(11212);
|
||
this.show = false;
|
||
let selectedData = [];
|
||
this.selectedArr.forEach((el, index) => {
|
||
if (el) {
|
||
console.log(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: calc(100% - 110rpx);
|
||
position: fixed;
|
||
left: 0;
|
||
bottom: 0;
|
||
z-index: 999;
|
||
padding: 10upx;
|
||
$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%;
|
||
height: 100%;
|
||
background-color: #ffffff;
|
||
&.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: calc(100% - 80rpx);
|
||
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> |