树形结构搜索栏实现-vuex-递归组件

465 阅读1分钟

思路:用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)