<template>
|
<view class="filterBar">
|
<view class="wrap flex f-a-i" :style="{'--top':`${top}rpx`}">
|
<view class="wrap-item flex f-a-i f-d-c" v-for="(item,index) in menu" :key="index"
|
:class="[item.selected?'active':'']">
|
<view class="wrap-item-box flex f-a-i" @click='select(item,index)'>
|
<view class="label">
|
{{item.label}}
|
</view>
|
<view class="item-icon" :class="[item.selected?'selected-item-icon':'']">
|
<u-icon color='' size='10' name="arrow-down-fill"></u-icon>
|
</view>
|
</view>
|
<view :class="[item.selected?'wrap-item-box-child':'']" :style="{top:calcTop}"
|
class="wrap-item-box-child-default">
|
<scroll-view scroll-y="true" class="scroll-Y">
|
<view class="type-line" v-if="item.type=='line'">
|
<view class="type-line-item flex f-a-i" v-for="(cItem,cIndex) in item.children"
|
:key="cIndex" @click='lineSelect(item,cItem,index,cIndex)'>
|
<view :class="[cItem.selected?'active':'']" class="type-label">{{cItem.label}}
|
</view>
|
<u-icon v-if="cItem.selected" color='#1180ff' size='16' name="checkbox-mark"></u-icon>
|
</view>
|
</view>
|
<view class="type-check" v-if="item.type=='check'">
|
<view class="type-check-item" v-for="(cItem,cIndex) in item.children" :key="cIndex">
|
<!-- 单选 -->
|
<view class="radio" v-if="cItem.type =='radio'">
|
<view class="radio-label">{{cItem.label}}</view>
|
<view class="option flex f-a-i">
|
<view class="option-item" v-for="(c_clItem,c_clIndex) in cItem.option"
|
:key="c_clIndex" :class="{'check_active':c_clItem.selected}"
|
@click='radioSelect(cItem,c_clItem)'>{{c_clItem.label}}</view>
|
|
</view>
|
</view>
|
<!-- 多选 -->
|
<view class="type-label radio" v-else>
|
<view class="radio-label">{{cItem.label}}</view>
|
<view class="option flex f-a-i">
|
<view class="option-item" :class="{'check_active':c_clItem.selected}"
|
v-for="(c_clItem,c_clIndex) in cItem.option" :key="c_clIndex"
|
@click='radioCheckSelect(cItem,c_clItem)'>{{c_clItem.label}}</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
</scroll-view>
|
<view class="btn-box flex f-a-i" v-if="item.type=='check'">
|
<view class="reset" @click="resetFilterData(index)">重置</view>
|
<view class="submit" @click="setFilterData(item)">确定</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
<view :class="[showOver?'show':'']" :style="{top:calcShadow}" @click="hideOverLay"></view>
|
</view>
|
</template>
|
|
<script>
|
export default {
|
name: "uFilter",
|
props: {
|
menuList: {
|
type: Array,
|
required: true,
|
default: () => []
|
},
|
defaultModel: {
|
type: Object,
|
required: true,
|
default: () => {}
|
},
|
top: {
|
type: Number,
|
default: 0
|
},
|
shadowTip: {
|
type: Number,
|
default: 36
|
}
|
},
|
data() {
|
return {
|
//显示遮罩层
|
showOver: false,
|
//数据值
|
model: {},
|
lineLabel: "",
|
menu: [],
|
}
|
},
|
computed: {
|
calcTop() {
|
const info = uni.getWindowInfo()
|
const windowTop = info.windowTop
|
const safeToTop = info.safeAreaInsets.top
|
const top = (windowTop + safeToTop + 36 - 8 + (this.top * 2)) + "rpx"
|
// return {
|
return top
|
// }
|
},
|
calcShadow() {
|
const info = uni.getWindowInfo()
|
const windowTop = info.windowTop
|
const safeToTop = info.safeAreaInsets.top
|
const top = (windowTop + safeToTop + 36 + 300 + (this.shadowTip * 2)) + "rpx"
|
return top
|
}
|
},
|
watch: {
|
menuList: {
|
handler(newVal) {
|
if (newVal) {
|
this.menu = JSON.parse(JSON.stringify(newVal))
|
this.init()
|
}
|
},
|
immediate: true,
|
deep: true
|
},
|
|
defaultModel: {
|
handler(newVal) {
|
if (newVal) {
|
this.model = JSON.parse(JSON.stringify(newVal))
|
this.defaultSelect()
|
this.$forceUpdate();
|
}
|
},
|
immediate: true,
|
deep: true
|
},
|
},
|
methods: {
|
//初始化数据父级、子集selected
|
init() {
|
try {
|
this.menu.forEach(v => {
|
if (!v.hasOwnProperty('selected')) {
|
this.$set(v, "selected", false)
|
}
|
if (v.type == 'line') {
|
v.children.forEach(clV => {
|
if (!clV.hasOwnProperty('selected')) {
|
this.$set(clV, "selected", false)
|
}
|
})
|
} else if (v.type == 'check') {
|
v.children.forEach(clV => {
|
clV.option.forEach(pv => {
|
if (!pv.hasOwnProperty('selected')) {
|
this.$set(pv, "selected", false)
|
}
|
})
|
})
|
}
|
})
|
} catch (e) {
|
console.log(e, "eeee")
|
}
|
},
|
//初始选中第一项
|
// defaultVal(data) {
|
// if (data.type == 'line') {
|
// this.model[data.prop] = data.children[0].value
|
// } else if (data.type == 'check') {
|
// data.children.forEach(v => this.model[v.prop] = v.option[0].value)
|
// }
|
// },
|
//根据初始值渲染第几项选中
|
defaultSelect() {
|
this.menu.forEach(v => {
|
if (v.type == 'line') {
|
const index = v.children.findIndex(cl => {
|
if (cl.value == this.model[v.prop]) {
|
return cl.value == this.model[v.prop]
|
}
|
})
|
//初始化所有选择项
|
v.children.forEach(l => l.selected = false)
|
if (index >= 0) {
|
v.children[index].selected = true
|
v.label = v.children[index].label
|
// this.lineLabel = v.children[index].label
|
this.$forceUpdate();
|
}
|
} else if (v.type == 'check') {
|
v.children.forEach(cl => {
|
if (cl.type == 'radio') {
|
const index = cl.option.findIndex(clO => {
|
if (clO.value == this.model[cl.prop]) {
|
return clO.value == this.model[cl.prop]
|
}
|
})
|
//初始化所有选择项
|
cl.option.forEach(cvo => cvo.selected = false)
|
if (index >= 0) {
|
cl.option[index].selected = true
|
this.$forceUpdate();
|
}
|
} else if (cl.type == 'checkbox') {
|
cl.option.forEach(cvo => cvo.selected = false)
|
if (this.model[cl.prop]) {
|
this.model[cl.prop].forEach(p => {
|
const index = cl.option.findIndex(clO => {
|
if (clO.value == p) {
|
return clO.value == p
|
}
|
})
|
if (index >= 0) {
|
cl.option[index].selected = true
|
this.$forceUpdate();
|
}
|
})
|
}
|
|
}
|
})
|
}
|
})
|
this.$forceUpdate();
|
},
|
//菜单栏选中
|
select(item, index) {
|
//重置所有的菜单选择状态
|
this.menu.forEach((v, vIndex) => {
|
if (vIndex != index) {
|
v.selected = false
|
} else {
|
v.selected = !v.selected
|
}
|
})
|
if (!item.selected) return this.showOver = false
|
this.showOver = true
|
this.$forceUpdate();
|
},
|
//行菜单子集选中
|
lineSelect(item, cItem, index, cIndex) {
|
this.menu[index].children.forEach((v, vIndex) => {
|
v.selected = false
|
})
|
cItem.selected = true
|
this.model[item.prop] = cItem.value
|
item.label = cItem.label
|
this.setFilterData()
|
},
|
//单选方法
|
radioSelect(item, clItem) {
|
if (clItem.selected) return
|
item.option.forEach(v => v.selected = false)
|
this.$set(clItem, 'selected', true)
|
// clItem.selected = true
|
this.model[item.prop] = clItem.value
|
this.$forceUpdate()
|
},
|
//多选方法
|
radioCheckSelect(item, clItem) {
|
if (!this.model[item.prop]) {
|
this.model[item.prop] = []
|
}
|
clItem.selected = !clItem.selected
|
if (clItem.selected) {
|
this.model[item.prop].push(clItem.value)
|
} else {
|
const index = this.model[item.prop].findIndex(v => v == clItem.value)
|
this.model[item.prop].splice(index, 1)
|
}
|
this.$forceUpdate()
|
},
|
//重置
|
resetFilterData(index) {
|
this.redioReset(index)
|
this.$emit("reset", this.model)
|
this.hideOverLay()
|
this.$forceUpdate()
|
},
|
//单选/多选重置
|
redioReset(index) {
|
const props = this.menu.filter(v => v.type == 'line').map(m => m.prop)
|
this.menu[index].children.forEach(v => {
|
//判断是否有type为line的状态栏 取出props
|
if (props.length > 0) {
|
props.forEach(p => {
|
// !this.defaultModel[v.prop]
|
if (p != v.prop && !this.defaultModel.hasOwnProperty(v.prop)) {
|
delete this.model[v.prop]
|
} else if (p != v.prop && this.defaultModel.hasOwnProperty(v.prop)) {
|
this.model[v.prop] = this.defaultModel[v.prop]
|
}
|
})
|
} else {
|
if (!this.defaultModel.hasOwnProperty(v.prop)) {
|
delete this.model[v.prop]
|
} else if (this.defaultModel.hasOwnProperty(v.prop)) {
|
this.model[v.prop] = this.defaultModel[v.prop]
|
}
|
}
|
v.option.forEach(op => op.selected = false)
|
if (v.type == 'radio') {
|
const index = v.option.findIndex(clO => {
|
return clO.value == this.model[v.prop]
|
})
|
v.option.forEach(cvo => cvo.selected = false)
|
if (index >= 0) {
|
v.option[index].selected = true
|
}
|
} else if (v.type == 'checkbox') {
|
v.option.forEach(cvo => cvo.selected = false)
|
if (this.model[v.prop]) {
|
this.model[v.prop].forEach(p => {
|
const index = v.option.findIndex(clO => {
|
return clO.value == p
|
})
|
if (index >= 0) {
|
v.option[index].selected = true
|
}
|
})
|
}
|
}
|
})
|
},
|
//确定
|
setFilterData(item) {
|
this.$emit("submit", this.model)
|
this.hideOverLay()
|
this.$forceUpdate()
|
},
|
hideOverLay() {
|
this.menu.forEach(v => {
|
v.selected = false
|
})
|
this.showOver = false
|
this.$forceUpdate()
|
}
|
}
|
}
|
</script>
|
|
<style scoped lang="scss">
|
.filterBar {
|
width: 100%;
|
background-color: #fff;
|
|
.flex {
|
display: flex;
|
}
|
|
.f-d-c {
|
flex-direction: column;
|
}
|
|
.f-a-i {
|
align-items: center;
|
}
|
|
.active {
|
color: #1180ff !important;
|
}
|
|
.check_active {
|
color: #fff !important;
|
background-color: #1180ff !important;
|
border: none !important;
|
}
|
|
.wrap {
|
z-index: 3;
|
width: 100%;
|
font-size: 32rpx;
|
border-bottom: 2rpx solid #e8e8e8;
|
justify-content: space-around;
|
// padding-bottom: 20rpx;
|
height: 72rpx;
|
box-sizing: border-box;
|
|
|
|
&-item {
|
color: #333333;
|
text-align: center;
|
transition: color .3s ease;
|
|
.label {
|
margin-right: 8rpx;
|
font-size: 28rpx;
|
}
|
|
.item-icon {
|
color: #333333;
|
transition: transform .3s linear;
|
transform: rotate(0);
|
}
|
|
.selected-item-icon {
|
color: #1180ff;
|
transform: rotate(180deg);
|
}
|
|
&-box-child {
|
// top: 72rpx !important;
|
width: 100% !important;
|
opacity: 1 !important;
|
z-index: 999 !important;
|
visibility: visible !important;
|
}
|
|
&-box-child-default {
|
position: absolute;
|
// top: 16rpx;
|
top: 0;
|
left: 0;
|
right: 0;
|
opacity: 0;
|
background: #fff;
|
transition: opacity .1s linear;
|
z-index: -999;
|
visibility: hidden;
|
|
.scroll-Y {
|
height: 600rpx;
|
}
|
|
.btn-box {
|
justify-content: space-between;
|
height: 120rpx;
|
flex-direction: row !important;
|
align-items: center;
|
padding: 0 24rpx;
|
// position: fixed;
|
background: #fff;
|
width: 100%;
|
box-sizing: border-box;
|
|
>view {
|
width: 240rpx;
|
height: 60rpx;
|
border-radius: 80rpx;
|
border: solid 1rpx #5887f9;
|
align-items: center;
|
justify-content: center;
|
box-sizing: border-box;
|
line-height: 56rpx;
|
font-size: 36rpx;
|
}
|
|
.reset {
|
color: #1180ff;
|
}
|
|
.submit {
|
color: #fff;
|
background-color: #1180ff;
|
}
|
}
|
|
.type-line {
|
padding-bottom: 40rpx;
|
font-size: 28rpx;
|
|
.type-line-item {
|
color: #333333;
|
justify-content: space-between;
|
padding: 20rpx;
|
border-bottom: 2rpx solid #e8e8e8;
|
}
|
}
|
|
.type-check {
|
|
.type-check-item {
|
width: 100%;
|
text-align: left;
|
color: #333333;
|
font-size: 28rpx;
|
padding-bottom: 20rpx;
|
|
.radio {
|
padding: 0 24rpx;
|
|
.radio-label {
|
padding: 10rpx 0;
|
}
|
|
.option {
|
flex-wrap: wrap;
|
|
&-item {
|
min-width: 120rpx;
|
text-align: center;
|
box-sizing: border-box;
|
padding: 5rpx 40rpx;
|
height: 60rpx;
|
line-height: 46rpx;
|
background-color: #fff;
|
color: #333333;
|
border: 2rpx solid #e8e8e8;
|
margin-top: 16rpx;
|
margin-right: 32rpx;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
.show {
|
background-color: rgba(0, 0, 0, .5);
|
z-index: 2;
|
position: fixed;
|
top: calc(44px + env(safe-area-inset-top) + 72rpx);
|
bottom: 0;
|
left: 0;
|
right: 0;
|
}
|
}
|
</style>
|