uniapp 树状数据无限极 进行展示并选择

时间:2024-04-19 10:29:26

两个文件 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>