Vue移动端树形控件实现(递归组件)

news/2024/7/10 0:36:39 标签: js, vue, vue.js, html

写在前面

因项目中需要用到树形控件,第三方的组件也不满足项目的使用需求,就自己造了一个车轮子,用Vue递归组件的方式实现,代码简单易懂,直接复制即可使用。

效果图

在这里插入图片描述

子组件代码

<template>
  <ul v-if="data.length">
    <li v-for="(item, i) in data" :key="i" @click.stop="selectItem(item)" v-show="expandFlag">
      <div class="item">
        <!-- 展开的图标 -->
        <i class='expandIcon'
         	@click.stop="expandItem(item, i)"
        	:class="[
        		expandArr.includes(i) ? 'reduce' : 'add',
        		item.children && item.children.length ? '' : 'disabled'
        	]">
        </i>
        <!-- 选项名 -->
        <h1>{{item[labelKey]}}</h1>
        <!-- 选择的图标 -->
        <i class='selectIcon'
        	:class="[
        		value[valueKey] == item[valueKey] ? 'checked' : 'noChecked',
        		 item[disabledKey] ? 'disabled' : ''
       		]">
       	</i>
      </div>
      <list-menu
        v-if='item.children'
        @input='input'
        :data='item.children'
        :valueKey='valueKey'
        :labelKey='labelKey'
        :disabledKey='disabledKey'
        :value="value"
        :toastText='toastText'
        :expandFlag='expandArr.includes(i)'
      />
    </li>
  </ul>
</template>
<script>
  export default {
    // 组件名必写
    name: 'ListMenu',
    props: {
      // 选中的值的属性名,必传
      valueKey: String,
      // 在页面要展示的选项属性名,必传
      labelKey: String,
      // 不可选的唯一标识,如item[disabledKey]未true则不可选择,非必传
      disabledKey: String,
      // 选中的值,必传
      value: Object,
      // 控制展开,不需要传
      expandFlag: {
        type: Boolean,
        default: true
      },
      // 总数据,必传
      data: Array,
      // 不可选提示文字,非必传
      toastText: String
    },
    data () {
      return {
        // 当前级组件已展开的项
        expandArr: []
      }
    },
    methods: {
      // 子组件逐级传递选中项
      input (item) {
        this.$emit('input', item)
      },
      // 选择
      selectItem (item) {
        // industryDeptType为1表示时不可选择该工会
        if (this.disabledKey && item[this.disabledKey]) {
          if (this.toastText) {
            alert(this.toastText)
          }
          return
        }
        this.$emit('input', item)
      },
      // 展开
      expandItem (item, i) {
        if (item.children && item.children.length) {
          let index = this.expandArr.indexOf(i)
          if (index > -1) {
            this.expandArr.splice(index, 1)
          } else {
            this.expandArr.push(i)
          }
        }
      }
    }
  }
</script>
<style lang="less" scoped>
  ul{
    width: 100%;
    color: #2a2a2a;
    font-size: 26/75rem;
    overflow: hidden;
    background: #fff;
    border-bottom: .8px solid #e1e1e1;
    li{
      .item{
        padding: 14/75rem 24/75rem;
        display: flex;
        align-items: center;
        width: 100%;
        .expandIcon{
          height:34/75rem;
          width:34/75rem;
          border: 1.5px solid;
          border-radius: 50%;
          position:relative;
          &:after{
            position: absolute;
            top: 50%;
            left: 50%;
            font-size: 34/75rem;
            transform: translate(-50%, -50%);
          }
          &.add{
            border-color: #2a2a2a;
            &:after{
              color: #2a2a2a;
              content: '+';
            }
          }
          &.reduce{
            border-color: #ff6633;
            &:after{
              color: #ff6633;
              content: '-';
            }
          }
          &.disabled{
            border-color: #ddd;
            &:after{
              color: #ddd;
            }
          }
        }
        .selectIcon{
          height:34/75rem;
          width:34/75rem;
          border: 1.5px solid;
          border-radius: 50%;
          position:relative;
          &:after{
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
          }
          &.checked{
            border-color: #ff6633;
            background: #ff6633;;
            &:after{
              font-size: 24/75rem;
              color: #fff;
              content: '✓';
            }
          }
          &.noChecked{
            border-color: #ff6633;
          }
          &.disabled{
            border-color: #ddd;
          }
        }
        h1{
          margin-right: 6/75rem;
          padding: 0 16/75rem;
          position: relative;
          top: 2/75rem;
          height: 60/75rem;
          line-height: 60/75rem;
          font-size:30/75rem;
          flex: 1;
          white-space: nowrap;
          color: #2a2a2a;
          // .one-line;
          overflow-x: auto;
        }
      }
      &:not(:first-child){
        border-top: .8px solid #e1e1e1;
      }
      >ul{
        border-bottom: 0;
        padding-left: 60/75rem;
        li{
          .item{
            padding-left: 12/75rem;
          }
          border-top: .8px solid #e1e1e1;
        }
      }
    }
  }
</style>


父组件调用代码

<template>
  <div class="lists">
    <list-menu
      :data='lists'
      v-model="selectVal"
      value-key='treeId'
      label-key='name'
      disabled-key='disabled'
      toastText='该选项不可选择'
    />
    <article>
      <p>选中的选项名:<span>{{selectVal.name || '未选择'}}</span></p>
      <p>选中的选项值:<span>{{selectVal.treeId || '未选择'}}</span></p>
    </article>
  </div>
</template>
<script>
  // 引入组件
  import ListMenu from './ListMenu'
  export default {
    components: {
      ListMenu
    },
    data () {
      return {
        selectVal: {},
        lists: []
      }
    },
    created () {
      this.getTreeList()
    },
    methods: {
      getTreeList () {
        // 模拟数据
        this.lists.push({
          treeId: 1,
          name: '第一级',
          disabled: 0,
          children: [{
            treeId: 2,
            name: '第二级(disadbed为true不可选)',
            disabled: 1,
            children: [
              {
                treeId: '3_1',
                name: '第三级(1)',
                disabled: 0
              },
              {
                treeId: '3_2',
                name: '第三级(2)',
                disabled: 0,
                children: [{
                  treeId: 4,
                  name: '第四级',
                  disabled: 0,
                  children: [{
                    treeId: 5,
                    name: '第五级',
                    disabled: 0,
                    children: [{
                      treeId: 6,
                      name: '第六级',
                      disabled: 0
                    }]
                  }]
                }]
              }
            ]
          }]
        })
      }
    }
  }
</script>
<style lang="less" scoped>
  .lists{
    article{
      margin: 30/75rem;
      line-height: 1.5;
      font-size: 30/75rem;
      span{
        color: #ff6633;
      }
    }
  }
</style>

觉得有用就给我点个赞吧,蟹蟹,(●ˇ∀ˇ●)


http://www.niftyadmin.cn/n/1410428.html

相关文章

Delphi编辑器使用指南

1.使用code explorer,双击可以查找方法的定义和实现。 2.使用CtrlShiftC,Class Completion,可以自动生成类和方法的定义。 3.Code Completion&#xff0c;可以自动提示&#xff0c;Ctrlspacebar. 4.CtrlShifti,可以对代码集缩进。CtrlShiftU&#xff0c;可以退回缩进。 本文转自…

Vue中使用contenteditable属性撸一个可以自动手动换行的输入框

写在前面 有时候页面上需要一个类似于微信中发送消息的输入框&#xff0c;即可以实现&#xff1a;默认只有一行文字高度&#xff0c;用户输入超过自动换行&#xff0c;用户也可以点击Enter键盘换行&#xff0c;当输入的内容清空后&#xff0c;输入框又恢复成一行的高度&#x…

centos7 修改时区

[rootbogon ~]# timedatectl set-timezone Asia/Shanghai #时区 [rootbogon ~]# ntpdate time1.aliyun.com #时间 本文转自 小小三郎1 51CTO博客&#xff0c;原文链接&#xff1a;http://blog.51cto.com/wsxxsl/1929837&#xff0c;如需转载请自行联系原作者

前端项目中常用JS方法封装分享

写在前面 我在写项目时&#xff0c;会把经常用到的方法专门用一个JS文件来管理&#xff0c;下面是我项目中经常用的一些方法 1、日期格式化&#xff08;时间戳转年月日时分秒&#xff09;&#xff1b; 2、千分位格式化&#xff08;10000转10,000&#xff09;&#xff1b; 3、E…

Xendesktop配置高可用DDC

桌面虚拟化产品目前主要的是Citrix的Xendesktop&#xff0c;VMware的View两大产品。今天主要介绍Xendesktop产品&#xff0c;关于View以后有机会了再做介绍。 关于Xendesktop的网上的教程太多了&#xff0c;而且现在Citrix已经将Xendesktop的安装做的越来越简单方便了&#xff…

手机靓号高亮效果(Vue)

效果 效果如下&#xff0c;靓号部分会显示红色 实现思路 1、定义正则规则 2、遍历手机号&#xff0c;生成一个由0和1组成的11位字符串&#xff0c;1表示靓号&#xff0c;0不为靓号 3、在HTML中循环手机号&#xff0c;根据下标给数字添加类名light&#xff0c;即高亮 JS代码…

(笔记)Socket设置非阻塞方式

1. 在linux C中可以将socket设置为非阻塞方式,代码: int cflags fcntl(socket_df,F_GETFL,0); fcntl(socket_df,F_SETFL, cflags|O_NONBLOCK); 当然也可以将socket设置为阻塞方式,代码: int cflags fcntl(socket_df,F_GETFL,0); fcntl(socket_df,F_SETFL, cflags&~O_NONB…

TikTok品牌出海:打造独特内容,提升品牌影响力

随着社交媒体的迅猛发展&#xff0c;TikTok作为全球最热门的短视频平台之一&#xff0c;为品牌出海提供了独特的机遇。然而&#xff0c;要在TikTok上成功推广品牌&#xff0c;关键在于创造出引人注目、有吸引力的内容。本文Nox聚星将和大家探讨在TikTok上&#xff0c;什么样的内…