zoukankan      html  css  js  c++  java
  • vue 移动端实现tree结构数据选择 (可多选、可单选)

    第一步 先创建组件目录结构 

    第二步 封装组件

    index.vue

    <template>
      <div class="tree-select-box">
        <air-cell type="text" :label="label" :placeholder="placeholder" v-model="text" @click="onSelect"></air-cell>
        <mt-popup v-model="showPopup" position="bottom" :closeOnClickModal="closeOnClickModal">
          <div class="popup-box">
            <div class="popup-header">
              <span @click="handleCancel">取消</span>
              <span class="title">{{ label }}</span>
              <span @click="handleConfirm">确认</span>
            </div>
            <div class="popup-content">
              <Tree ref="tree" :data="showData"/>
            </div>
          </div>
        </mt-popup>
      </div>
    </template>
    <script>
    import { MessageBox } from 'mint-ui'
    import { filterMultipleData } from '@/utils/air-service/commonMethods.js'
    import { mannagerModule, airServiceModule } from '@/service/api/commonApi.js'
    import { getStorage } from 'hr-utils'
    import AirCell from '@/components/air-service/common/search/cell.vue'
    import Tree from './tree.vue'
    export default {
      name: 'treeSelect',
      components: {
        AirCell,
        Tree
      },
      props: {
        label: {
          type: String,
          default: '所属部门'
        },
        // 展示的文字内容
        text: {
          type: [String, Number],
          default: ''
        },
        value: {
          type: [String, Number],
          default: ''
        },
        // 默认是部门
        type: {
          type: String,
          default: ''
        },
        placeholder: {
          type: String,
          default: '请选择'
        },
        loading: {
          type: Boolean,
          default: false
        },
        data: {
          type: Array,
          default () {
            return []
          }
        },
        // 兼容不同字段的tree数据
        treeParams: {
          type: Object,
          default () {
            return {
              id: 'id',
              name: 'label',
              children: 'children'
            }
          }
        },
        // 是否展开子级
        showTree: {
          type: Boolean,
          default: false
        },
        // 处理单选和多选
        checkbox: {
          type: Boolean,
          default: false
        },
        // 是否可点击遮罩层关闭
        closeOnClickModal: {
          type: Boolean,
          required: false,
          default: function () {
            return true
          },
        },
      },
      provide() {
        return {
          treeParams: this.treeParams,
          showTree: this.showTree,
          checkbox: this.checkbox,
          type: this.type
        }
      },
      data () {
        return {
          showPopup: false,
          showData: this.data,
          // 用户信息
          userinfo: getStorage('userinfo') 
        }
      },
      created () {
        this.getShowData()
      },
      methods: {
        // 选择内容
        onSelect () {
          this.showPopup = true
        },
        // 取消按钮
        handleCancel () {
          this.showPopup = false
          this.$emit('cancel')
        },
        // 确认按钮
        handleConfirm () {
          const { id, name } = this.treeParams
          this.selectData = this.$refs.tree.getSelectData()
          if (!this.selectData.length) {
            MessageBox('温馨提示', '请选择数据')
            return
          }
          let selectId = []
          let selectName = []
          this.showPopup = false
          this.selectData.map(item => {
            selectId.push(item[id])
            selectName.push(item[name])
          })
          this.upData(selectName.join(','), selectId.join(','))
        },
        // 更新值
        upData (text, value) {
          this.$emit('update:text', text)
          this.$emit('update:value', value)
          this.$emit('change', { text: text, value: value })
          this.$nextTick(() => {
            this.$refs.tree.setSelectData(value.split(','))
          })
        },
        // 获取展示数据
        async getShowData () {
          try {
            const { id, name } = this.treeParams
            this.$emit('update:loading', true)
            // 部门
            if (this.type === 'department') {
              const res = await mannagerModule.getUserMDept()
              this.showData = filterMultipleData([...res.data] || [])
              // 设置默认值
              let { pk_dept = '', pk_deptdoc1 = '' } = this.userinfo
              let ids = []
              let names = []
              if (this.checkbox) { // 部门多选设置默认值
                if (pk_deptdoc1) {
                  ids.push(pk_deptdoc1)
                }
                if (pk_dept) {
                  ids.push(pk_dept)
                }
              } else { // 部门单选设置默认值
                if (pk_deptdoc1) {
                  ids.push(pk_deptdoc1)
                } else if (pk_dept) {
                  ids.push(pk_dept)
                }
              }
              res.data.map(item => {
                if ( ids.includes(item[id]) ) {
                  names.push(item[name])
                }
              })
              this.upData(names.join(','), ids.join(','))
            }
            // 人员类别
            if (this.type === 'category') {
              const res = await airServiceModule.queryReferencePsn({
                refType: 'pk_psncl',
                test: 'test',
              })
              let list = res && res.data && res.data.body ? res.data.body : []
              list = list.slice(1) || [] // 第一条是目录,剔除掉
              this.showData = list
              if (this.showData.length) {
                this.upData(list[0][name], list[0][id])
              }
            }
          } catch (error) {
            
          } finally {
            this.$emit('update:loading', false)
          }
        }
      }
    }
    </script>
    <style lang="less" scoped>
    .popup-box {
       100vw;
      height: 50vh;
      display: flex;
      flex-direction: column;
      .popup-header {
        height: 0.9rem;
        display: flex;
        flex-shrink: 0;
        align-items: center;
        justify-content: space-between;
        padding: 0 0.3rem;
        border-bottom: 1px solid #f2f2f2;
        span {
          color: #b4c4d4;
        }
        span.title {
          color: #333333;
        }
      }
      .popup-content {
        flex-grow: 1;
        overflow: auto;
        padding: 0.3rem;
        box-sizing: border-box;
      }
    }
    </style>

    tree.vue

    <template>
      <div class="popup-select-tree">
        <tree-item
          v-for="node in treeData" 
          :key="node[treeParams.id]" 
          :node="node"
          :treeParams="treeParams"
          @selectNode="selectNode">
        </tree-item>
      </div>
    </template>
    <script>
    import treeItem from './tree-item.vue'
    export default {
      name: 'popup-select-tree',
      components: {
        treeItem
      },
      inject: ['treeParams', 'checkbox'],
      props: {
        data: {
          type: Array,
          default () {
            return []
          }
        }
      },
      watch: {
        data: {
          handler(newVal, oldVal) {
            this.treeData = newVal
          },
          deep: true,
        }
      },
      data () {
        return {
          treeData: this.data,
          // 模拟数据
          data2: [{
            id: 1,
            label: '一级 1',
            children: [{
              id: 4,
              label: '二级 1-1',
              children: [{
                id: 9,
                label: '三级 1-1-1'
              }, {
                id: 10,
                label: '三级 1-1-2'
              }]
            }]
          }, {
            id: 2,
            label: '一级 2',
            children: [{
              id: 5,
              label: '二级 2-1'
            }, {
              id: 6,
              label: '二级 2-2'
            }]
          }, {
            id: 3,
            label: '一级 3',
            children: [{
              id: 7,
              label: '二级 3-1'
            }, {
              id: 8,
              label: '二级 3-2',
              children: [{
                id: 11,
                label: '三级 3-2-1'
              }, {
                id: 12,
                label: '三级 3-2-2'
              }, {
                id: 13,
                label: '三级 3-2-3'
              }]
            }]
          }],
          // 存放选择的数据
          selects: [],
          // 用于记录单选上一次选中的节点,作清除处理
          oldNode: null
        }
      },
      methods: {
        // 仅支持父子级非严格关联情况,严格关联未开发
        selectNode (node, type = '') {
          const { children } = this.treeParams
          let nodeChildren = node[children]
          if (this.checkbox) { // 多选
            // 判断是否已选中该节点,有则删除、没有则添加
            if (node.checkbox && !type) {
              this.$set(node, 'checkbox', false) // 取消选中状态
            } else {
              if (!node.checkbox) {
                this.$set(node, 'checkbox', true) // 设置选中状态
              }
              // 选择父级节点默认将子节点也选中
              if (nodeChildren && nodeChildren.length) {
                nodeChildren.map(nodes => {
                  this.selectNode(nodes, 'add')
                })
              }
            }
          } else { // 单选
            if (this.oldNode) {
              this.$set(this.oldNode, 'checkbox', false)
            }
            this.$set(node, 'checkbox', true)
          }
          this.oldNode = node
        },
        handleSelectData (data = this.treeData) {
          const { children } = this.treeParams
          data.map(item => {
            if (item.checkbox) {
              this.selects.push({...item})
            }
            if (item[children] && item[children].length) {
              this.handleSelectData(item[children])
            }
          })
        },
        // 获取选中状态的数据
        getSelectData () {
          this.selects = []
          this.handleSelectData()
          console.log('selects', this.selects)
          return this.selects
        },
        // 设置选中值
        setSelectData (ids, data = this.treeData) {
          const { id, children } = this.treeParams
          data.map(node => {
            let nodeChildren = node[children]
            this.$set(node, 'checkbox', false)
            if (ids.includes(node[id])) {
              this.oldNode = node
              this.$set(node, 'checkbox', true)
            }
            if (nodeChildren && nodeChildren.length) {
              this.setSelectData(ids, nodeChildren)
            }
          })
        }
      }
    }
    </script>

    tree-item.vue

    <template>
      <div class="tree-item-box">
        <div class="tree-item-list">
          <span class="arrow-box">
            <img 
              v-if="node[treeParams.children] && node[treeParams.children].length" 
              class="arrow" 
              :class="{'arrow-down': show}"  
              :src="arrowIcon" 
              alt="" 
              @click="showchildren()">
          </span>
          <span class="name" @click="selectNode(node)">{{ node[treeParams.name] }}</span>
          <img class="tick" :src="tickTrue" v-show="node.checkbox" alt="" @click="selectNode(node)">
        </div>
        <tree-item 
          v-show="show" 
          class="tree-children" 
          v-for="item in node[treeParams.children]" 
          :key="item[treeParams.id]"
          :node="item"
          @selectNode="selectNode"/>
      </div>
    </template>
    <script>
    /*
     * showTree false为不展示子级 true展示子级
     * checkbox false为单选 true为多选
     */
    export default {
      name: 'tree-item',
      inject: ['treeParams', 'showTree', 'checkbox'],
      props: {
        node: {
          type: Object,
          default() {
            return {};
          }
        }
      },
      data () {
        return {
          show: this.showTree,
          tick: require('../../../../../static/img/pages/airService/tick.png'),
          tickTrue: require('../../../../../static/img/pages/airService/tick-true.png'),
          arrowIcon: require('../../../../../static/img/pages/airService/arrow.png'),
        }
      },
      methods: {
        // 显示或隐藏子级
        showchildren () {
          this.show = !this.show
        },
        // 选择树节点
        selectNode (node) {
          this.$emit('selectNode', node)
        }
      }
    }
    </script>
    <style lang="less" scoped>
    .tree-item-box {
      .tree-item-list {
        display: flex;
        align-items: center;
        height: 0.5rem;
        .arrow-box {
          display: flex;
          align-items: center;
           0.3rem;
          .arrow {
             100%;
            &.arrow-down {
              transform: rotate(90deg);
            }
          }
        }
        .name {
          margin-left: 0.1rem;
          flex-grow: 1;
        }
        .tick {
           0.5rem;
          margin-left: auto;
        }
      }
      .tree-children {
        margin-left: 0.2rem;
      }
    }
    </style>

     第三步引入组件并使用

     效果预览

  • 相关阅读:
    JDBC---bai
    下拉列表---demo---bai
    智能提示框---bai
    国际化---demo1---bai
    自定义数据校验(4)---demo3---bai
    数据校验(3)--demo2---bai
    json概述
    redis持久化
    MyBatis中动态SQL语句完成多条件查询
    Jedis连接redis的一些基本操作
  • 原文地址:https://www.cnblogs.com/fczbk/p/15233936.html
Copyright © 2011-2022 走看看