两个文件 demo.vue – olylis-cascader.vue
提示:可以对照接口返回的参数名来修改,把value=id,label=name
demo.vue
<template>
<olylis-cascader :options="treeData" :value="selectedValue" @change="handleChange" :is-show-nav="false">
<!-- 可自定义节点 -->
<!-- <template v-slot="{ data }">-->
<!-- <view>-->
<!-- {{ data.label }} : {{ data.value }}-->
<!-- </view>-->
<!-- </template>-->
</olylis-cascader>
</template>
<script>
// 第二个文件的地址,自己修改
import olylisCascader from './olylis-cascader'
export default {
name: 'demo',
components: {olylisCascader},
data() {
return {
selectedValue: [],
treeData: [
{
value: 1,
label: '一级 1',
children: [{
value: 11,
label: '二级 1-1',
children: [{
value: 111,
label: '三级 1-1-1'
}]
}],
}, {
value: 2,
label: '一级 2',
children: [{
value: 21,
label: '二级 2-1',
children: [{
value: 211,
label: '三级 2-1-1'
}]
}, {
value: 22,
label: '二级 2-2',
children: [{
value: 221,
label: '三级 2-2-1'
}]
}]
}, {
value: 3,
label: '一级 3',
children: [{
value: 31,
label: '二级 3-1',
children: [{
value: 311,
label: '三级 3-1-1'
}]
}, {
value: 32,
label: '二级 3-2',
children: [{
value: 321,
label: '三级 3-2-1'
}]
}]
}
]
}
},
//现在这里写死,可以自己加个接口换数据
methods: {
// 每次点击都会返回点击的内容
handleChange(selectedValue, clickItem) {
console.log('handleChange', selectedValue, clickItem)
this.selectedValue = selectedValue
}
}
}
</script>
<style scoped>
</style>
olylis-cascader.vue
这里我标下可能需要替换参数名的地方,如果是需要id,name 之类的
html代码应该都能看懂,知道那些替换,我重点是下面的方法里是标注下
<template>
<view ref="cascaderRef" class="cascader">
<view v-if="isShowNav" class="bread-crumb clearfix">
<view class="bread-crumb-root">当前:</view>
<view class="bread-crumb-item" v-for="(item, index) in breadCrumbList" :key="item.value" >
<text class="bread-crumb-text" @click="clickNav(item, index)">{{item.label}}</text>
<view v-if="index < breadCrumbList.length - 1" class="bread-crumb-icon">></view>
</view>
</view>
<view class="cascader-content">
<scroll-view scroll-x scroll-with-animation :scroll-left="scrollLeft" style="height:100%;">
<view class="options-row clearfix" :style="rowStyle">
<view class="options-column" v-for="(opitons, columnIndex) in optionsList" :key="columnIndex" :style="columnStyle">
<scroll-view scroll-y style="height:100%">
<view class="options-item" v-for="item in opitons" :class="{'active': selectedValue[columnIndex] === item.value}" :key="item.value" @click="clickItem(item, columnIndex)">
<slot :data="item">{{item.label}}</slot>
</view>
</scroll-view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
function getStyleString (styleObject) {
return Object.keys(styleObject).map(propName => (propName + ':' + styleObject[propName] + ';')).join('')
}
export default {
name: 'cascader',
props: {
columnWidth: {
type: Number,
default: null
},
value: {
type: Array,
default() {return []}
},
options: {
type: Array,
default() {return []}
},
isShowNav: {
type: Boolean,
default: true
}
},
data() {
return {
WIDTH: 300,
COLUMN_WIDTH: 150,
selectedValue: [],
optionsList: []
}
},
computed: {
breadCrumbList() { // id
return this.selectedValue.map((item, i) => this.optionsList[i].find(option => option.value === item))
},
rowStyle() {
return getStyleString({
width: this.COLUMN_WIDTH * (Math.max(2, this.optionsList.length)) + 'px'
})
},
columnStyle() {
return getStyleString({
width: this.COLUMN_WIDTH + 'px'
})
},
scrollLeft() {
return Math.max(0, this.COLUMN_WIDTH * (this.optionsList.length - 2))
}
},
watch: {
value(val) {
this.handleValue(val)
},
options(val) {
this.handleValue(this.value)
}
},
created() {
this.handleValue(this.value)
},
mounted() {
this.init()
},
methods: {
init() {
const query = uni.createSelectorQuery().in(this);
query.select('.cascader').boundingClientRect(data => {
this.WIDTH = data.width ? data.width : 150
this.COLUMN_WIDTH = this.WIDTH / 2
}).exec();
},
handleValue(val) {
this.selectedValue = []
this.optionsList = this.getOptionsList(val, this.options)
this.selectedValue = val
},
getOptionsList(values, options, currentList = []) {
if (!options || options.length === 0) return currentList
currentList.push(options)
if (values.length === 0) return currentList
// id
const next = options.find(item => item.value === values[0])
const nextValue = values.slice(1)
const nextOptions = next.children
return this.getOptionsList(nextValue, nextOptions, currentList)
},
clickNav(item, index) {
this.clickItem(item, index)
},
clickItem(item, columnIndex) {
const selectedValue = this.selectedValue.slice(0, columnIndex)
const optionsList = this.optionsList.slice(0, columnIndex + 1)
// id
selectedValue.splice(columnIndex, 1, item.value)
this.selectedValue = selectedValue
if (item.children) optionsList.splice(columnIndex + 1, 1, item.children)
this.optionsList = optionsList
this.$emit('change', selectedValue, item)
}
}
}
</script>
<style lang="scss" scoped>
.clearfix::after {
content: "";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.cascader {
height: 100%;
.bread-crumb {
height: 30px;
line-height: 30px;
border-bottom: 1px solid #d8d8d8;
padding: 0 10px;
font-size: 12px;
.bread-crumb-root {
float: left;
}
.bread-crumb-item {
float: left;
}
.bread-crumb-text {
color: #0083FF;
}
.bread-crumb-icon {
display: inline-block;
padding: 0 5px;
}
}
.cascader-content {
height: calc(100% - 30px);
}
.options-row {
height: 100%;
}
.options-column {
float: left;
height: 100%;
box-sizing: border-box;
&:not(:last-child) {
border-right: 1px solid #d8d8d8;
}
}
.options-item {
padding: 10px 10px;
&.active {
color: #ff6600;
}
}
}
</style>