思路:用vuex存储选中的选项,通过compute返回选择中的数组,利用includes判断是否选择状态。递归组件,定义字段(不会冲突,组件树节点自己管理)showPanel来显示下拉上拉。
测试数据结构
data: [
{
id: 1,
label: '测试1',
children: [
{
id: '1-1',
label: '测试1-1',
children: [
{
id: '1-1-1',
label: '测试1-1-1'
}
] },
{
id: '1-2',
label: '测试1-2',
children: [
{ id: '1-2-1', label: '测试1-2-1' },
{ id: '1-2-2', label: '测试1-2-2' }] },
{
id: '1-3',
label: '测试1-3',
children: []
}]
}
]
<template> <div class="camera-select-container"> <div class="camera-select-box"> <div class="icon-camera"><img src="@/assets/icon-camera.png" alt=""></div> <div class="select-input "> <el-tooltip class="item" effect="dark" :content="checkLabel" placement="top-start"> <div class="font-overflow-1">{{ checkLabel }}</div> </el-tooltip> </div> <div :class="[showPanel?'triangle-down':'triangle-up','triangle']" @click="showPanel=!showPanel" /> <div :class="[showPanel?'show-select-panel':'','select-panel']"> <div class="select-box cunstom-scrollbar"> <div class="" style="max-height:500px"> <cameraTree :data="data" @check="check" /> </div> </div> </div> </div> </div></template>
<script>import cameraTree from './cameraTree'import { mapGetters } from 'vuex'export default { name: 'CameraSelect', components: { cameraTree }, data() { return { showPanel: false, checkIds: [], data: [ { id: 1, label: '测试1', children: [ { id: '1-1', label: '测试1-1', children: [ { id: '1-1-1', label: '测试1-1-1' } ] }, { id: '1-2', label: '测试1-2', children: [ { id: '1-2-1', label: '测试1-2-1' }, { id: '1-2-2', label: '测试1-2-2' } ] }, { id: '1-3', label: '测试1-3', children: [] } ] } ] } }, computed: { ...mapGetters(['checkCameraLabels']), checkLabel() { if (this.checkCameraLabels.length === 0) { return '请选择摄像头' } else { return this.checkCameraLabels.join(',') } } }, methods: { check(id) { const index = this.checkIds.indexOf(id) if (index > -1) { this.checkIds.splice(index, 1) } else { this.checkIds.push(id) } } }}</script>
1、check方法监听子组件check事件,处理选择节点事件
2、computed=》checkLabel显示input框选中的节点
递归组件cameraTree
<template> <div> <div class="camera-list"> <div v-for="(item,index) in handerData" :key="index" class="camera-item"> <div class="camera-title-box"> <div v-if="item.children&&item.children.length>0" :class="[item.hidePanel?'triangle-up':'triangle-down','triangle']" @click="showPanel(item,index)" /> <!-- 样式占位符 --> <div v-else class="no-children" /> <div :class="[checkCameraIds.includes(item.id)?'check':'no-check','icon-check']" @click="check(item)" /> <div class="title" @click="check(item)">{{ item.label }}</div> </div> <div v-if="item.children" class="children-box"> <div :class="['show-children-list',item.hidePanel?'':'hide-panel']"><CameraTree :data="item.children" /></div> </div> </div> </div> </div></template><script>import { mapGetters, mapActions } from 'vuex'export default { name: 'CameraTree', props: { data: { type: Array, default: () => [] } }, data() { return { isCheck: false, handerData: [] } }, computed: { ...mapGetters(['checkCameraIds']) }, watch: { data: { handler(val) { if (val) { const arr = [] val.forEach(e => { e.hidePanel = false arr.push(e) }) this.handerData = arr } }, immediate: true } }, methods: { ...mapActions({ checkCamera: 'hikvision/checkCamera' }), check(item) { const label = item.label const id = item.id const obj = { label, id } this.checkCamera(obj) }, showPanel(item, index) { item.hidePanel = !item.hidePanel this.$set(this.handerData, index, item) } }}</script>
1、选择节点调用check方法,触发vuex里的方法改变属性,父级元素的compute再计算出选择的label
2、处理初始数据,如果给的数据结构没有字段判断是否显示子节点(隐藏/显示动画-手风琴),则可以监听传入的data,对数据做一步处理。hidePanel字段就来控制是否显示子节点
vuex方法
const state = { checkCameraIds: [], checkCameraLabels: []}const mutations = { SET_cameraIds: (state, data) => { state.checkCameraIds = data }, SET_cameraLabels: (state, data) => { state.checkCameraLabels = data }}const actions = { checkCamera({ commit }, item) { const { id, label } = item const ids = [...state.checkCameraIds] const labels = [...state.checkCameraLabels] const index = ids.indexOf(id) if (index > -1) { ids.splice(index, 1) labels.splice(index, 1) } else { ids.push(id) labels.push(label) } commit('SET_cameraIds', ids) commit('SET_cameraLabels', labels) }}export default { namespaced: true, state, mutations, actions}
总结思路
选择节点 - 调用vuex-action方法 - 改变store里的属性 - 父元素重新计算属性(需要显示的label,搜索所需要的Id)