效果图(右箭头用的uviewplus的u-icon ,可自行修改)
此组件只包含画框部分
传入的数组格式如下(一个label、一个value、一个children组成的树形数组),回显只需将此数组以及目标id传入即可,
微信小程序的话隐藏再显示一次组件就通过refs调用一次组件的getScollWidthForMpWeixin方法即可
源代码如下:
<template>
<view class="next-scroll-box">
<scroll-view class="next-scroll-title" scroll-x="true" :scroll-left="scrollViewWidth" scroll-with-animation>
<view class="next-scroll-title-item-box" v-for="(i, e) in tabList" @click="checkTab(e)" :key="e">
<view
v-if="tabId >= e"
:id="'next-' + e"
:class="['next-scroll-title-item', tabId == e ? ' next-scroll-title-item-true' : '']">
{{ checkList[e] ? checkList[e][labelKey] : i.title }}
<view style="margin-left: 20rpx" v-if="tabId > e">
<u-icon size="14" bold color="#6D809D" name="arrow-right"></u-icon>
</view>
</view>
</view>
</scroll-view>
<scroll-view
class="next-scroll-view_H"
scroll-y="true"
:scroll-into-view="scrollIntoView"
scroll-with-animation>
<view
class="next-scroll-view-grid-box"
v-if="checkBox && checkBox.length && checkBox[tabId] && checkBox[tabId].length">
<view
:id="`next-scroll-view-item-` + index"
v-for="(item, index) in checkBox[tabId]"
:key="index"
@click="check(index)"
:class="
checkList && checkList[tabId] && checkList[tabId][labelKey] == item[labelKey]
? 'next-scroll-view-item-true'
: 'next-scroll-view-item'
">
{{ item[labelKey] || '' }}
</view>
</view>
<view class="next-scroll-view-noBox" v-else>
<view class="text">暂无数据</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
props: {
value: {
type: String,
default: '',
},
options: {
type: Array,
default: () => {
return []
},
},
valueKey: {
type: String,
default: 'value',
},
labelKey: {
type: String,
default: 'label',
},
childrenKey: {
type: String,
default: 'children',
},
defaultValue: {
// 默认值
type: [Number, String],
default: '',
},
},
data() {
return {
checkBox: [],
tabId: 0,
checkList: [],
checkListModel: [],
id: 0,
tabList: [
{
title: '请选择',
id: 0,
},
],
scrollViewWidth: 0,
elWidth: 0,
yIndex: 0,
scrollIntoView: '',
}
},
created() {},
options: {
styleIsolation: 'shared', // 解除样式隔离
},
mounted() {
this.init()
console.log('----------插件挂载完毕------------')
},
watch: {
defaultValue: {
immediate: true,
handler(newVal) {
if ((newVal || newVal == '0') && this.options.length) {
this.handleDefaultSelected(newVal, this.options)
}
},
},
},
computed: {
_value() {
return (this.checkListModel || []).map((item) => item[this.valueKey]).join(',')
},
},
methods: {
init() {
if (!this.defaultValue && this.defaultValue != 0) {
this.id = 0
this.tabId = 0
this.checkBox = []
this.checkList = []
}
//初始化求出滚动的宽度
let view = uni.createSelectorQuery().in(this).select('.next-scroll-title')
view.boundingClientRect((rect) => {
this.scrollViewWidth = Math.round(rect.width)
}).exec()
this.getData()
},
// 微信特调方法,初始设置scrollViewWidth
getScollWidthForMpWeixin() {
let view = uni.createSelectorQuery().in(this).select('.next-scroll-title')
view.boundingClientRect((rect) => {
this.scrollViewWidth = Math.round(rect.width)
}).exec()
let view2 = uni
.createSelectorQuery()
.in(this)
.select('#next-' + this.tabId)
view2
.boundingClientRect((rect) => {
this.elWidth = Math.round(rect.width)
})
.exec()
if (this.scrollIntoView) {
this.scrollIntoView = 'next-scroll-view-item-0'
}
setTimeout(() => {
this.scrollViewWidth = this.scrollViewWidth + this.elWidth
})
setTimeout(() => {
this.scrollIntoView = `next-scroll-view-item-` + this.yIndex
}, 100)
},
async check(index) {
this.$set(this.checkList, this.id, this.checkBox[this.id][index])
const children = this.checkBox[this.id][index][this.childrenKey]
let n = 0
if (children && children.length) {
n = this.id + 1
} else {
n = this.id
}
let arr = []
for (let i = 0; i <= n; i++) {
arr.push({
title: '请选择',
id: i,
})
}
this.tabList = arr
if (this.id < this.tabList.length - 1) this.id = this.id + 1
await this.getData()
let view = uni
.createSelectorQuery()
.in(this)
.select('#next-' + this.tabId)
view.boundingClientRect((rect) => {
this.elWidth = Math.round(rect.width)
}).exec()
setTimeout(() => {
this.scrollViewWidth = this.scrollViewWidth + this.elWidth
})
if (this.tabId < this.tabList.length - 1) this.tabId = this.tabId + 1
},
checkTab(e) {
if (e == this.id) return
this.id = e
this.tabId = e
this.checkList = this.checkList.splice(0, e)
this.scrollIntoView = 'next-scroll-view-item-0'
this.$emit('confirm', {
flag: false,
})
},
getResult(event) {
if (event == 'confirm') {
if (this.checkList.length != this.tabList.length) return
let result = this.checkList
this.checkListModel = result
// #ifdef VUE2
this.$emit('input', this._value)
// #endif
// #ifdef VUE3
this.$emit('update:value', this._value)
// #endif
// flag为true 即已经选到了最终节点
this.$emit('confirm', {
value: result,
flag: true,
})
}
},
//使用本地假数据进行加载
async getData() {
if (this.checkList.length === this.tabList.length) {
this.getResult('confirm')
// console.log('tabid------>', this.tabId)
// console.log('id----->', this.id)
// console.log('checkbox----->', this.checkBox)
// console.log('checkList----->', this.checkList)
// console.log('tabList----->', this.tabList)
return
}
// 此处的flag为false是告诉父级并没有选到最终的节点
this.$emit('confirm', {
flag: false,
})
let list = []
if (this.checkList.length) {
var id = this.checkList[this.id - 1][this.valueKey]
const item = this.checkBox[this.id - 1].find((item) => {
return item[this.valueKey] == id
})
;(item[this.childrenKey] ? item[this.childrenKey] : []).map((e) => {
list.push(e)
})
this.$set(this.checkBox, this.id, list)
} else {
this.options.map((e) => {
list.push(e)
})
this.$set(this.checkBox, this.id, list)
}
},
// 回显函数 传入value(要保证唯一) 以及总的数组
handleDefaultSelected(code, handlerArr) {
if (!code || !handlerArr || !Array.isArray(handlerArr) || !handlerArr.length) return
let that = this
function findNodeById(code, arr, path = []) {
for (let i = 0; i < arr.length; i++) {
if (arr[i][that.valueKey] == code) {
return [...path, arr[i]]
}
if (arr[i][that.childrenKey]) {
let result = findNodeById(code, arr[i][that.childrenKey], [...path, arr[i]])
if (result) {
return result
}
}
}
return null
}
const checkList = findNodeById(code, handlerArr)
if (checkList === null) return
let checkBox = [handlerArr]
const tabList = checkList.map((item, index) => {
if (item[that.childrenKey] && item[that.childrenKey].length > 0 && checkList.length > index + 1)
checkBox.push(item[that.childrenKey])
return { title: '请选择', id: index }
})
const id = tabList.length - 1
const tabId = tabList.length - 1
const yIndex = checkBox[checkBox.length - 1]?.findIndex(
(it) => it[that.valueKey] == checkList[checkList.length - 1][that.valueKey]
)
that.checkList = checkList
that.checkBox = checkBox
that.id = id
that.tabId = tabId
that.tabList = tabList
that.yIndex = yIndex
setTimeout(() => {
that.scrollIntoView = `next-scroll-view-item-` + yIndex
}, 100)
// console.log('checkList--->', checkList)
// console.log('checkBox--->', checkBox)
// console.log('id--->', id)
// console.log('tabId--->', tabId)
// console.log('tabList--->', tabList)
},
},
}
</script>
<style>
/deep/ ::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
display: none;
}
</style>
<style lang="scss" scoped>
.next-scroll-box {
width: 100%;
height: 100%;
background: #ffff;
border-radius: 24rpx 24rpx 0 0;
}
.next-scroll-title {
white-space: nowrap;
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #fafafa;
padding: 0 20rpx;
}
.next-scroll-view_H {
white-space: nowrap;
width: 100%;
height: 400rpx;
line-height: 100rpx;
background-color: #ffffff;
}
.next-scroll-title-item {
position: relative;
display: flex;
align-items: center;
}
.next-scroll-title-item-box {
display: inline-block;
margin: 0 10rpx;
font-size: 28rpx;
color: #333333;
}
.next-scroll-title-item-true {
font-size: 28rpx;
font-weight: 700;
color: #45afff;
}
.next-scroll-view-grid-box {
width: calc(100% - 20rpx);
margin: 10rpx;
padding-bottom: 10rpx;
}
.next-scroll-view-noBox {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.text {
width: 100%;
color: #333333;
text-align: center;
font-size: 28rpx;
}
}
.next-scroll-view-item {
padding: 0rpx 24rpx;
text-align: left;
border-radius: 6rpx;
background: #fff;
color: #333333;
font-size: 28rpx;
margin: 12rpx 4rpx;
height: 66rpx;
line-height: 66rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.next-scroll-view-item-true {
padding: 0rpx 24rpx;
text-align: left;
border-radius: 6rpx;
color: #45afff;
font-size: 28rpx;
margin: 12rpx 4rpx;
height: 66rpx;
line-height: 66rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>