基于Ant Design of Vue的级联导航菜单组件

1.显示效果如图

2.数据格式

3.组件代码

<template>
    <!-- 数据菜单 -->
  	<!-- 这里面只写到了第四层,
      需要用到多少,按照这个格式
      续写下去就行,核心就是如果
      下面还有一层,那么他只能是
      这个标签a-sub-menu,只有根
      节点才能用这个标签a-menu-item!
      另外感觉可以优化的地方是可以将
      template单独封装成组件,item.children作为组件值
    -->
    <a-menu
      ref="menu"
      style="width: 100%;min-height: 340px;"
      mode="horizontal"
      @click="switchMenu"
    >
      <template v-for="item in menuInfo">
        <a-sub-menu :key="item.id">
          <span slot="title">
            <span>{{item.meta.title}}</span>
          </span>
          <template v-for="inItem in item.children" >
            <!-- 二层没children子级 -->
            <a-menu-item v-if="!inItem.children"  :disabled="inItem.disabled" :key="inItem.id">
                <app-link :to="item.path + '/' + inItem.path">
                    {{inItem.meta.title}}
                </app-link>
            </a-menu-item>
            <!-- 二层有children子级 -->
            <a-sub-menu v-if="inItem.children" :key="inItem.id">
              <span slot="title"><span>{{inItem.meta.title}}</span></span>
              <template v-for="inItemTwo in inItem.children" >
                <!-- 三层没children子级 -->
                <a-menu-item v-if="!inItemTwo.children" :disabled="inItemTwo.disabled"  :key="inItemTwo.id">
                  <a :href="inItem.path + '/' + inItemTwo.path" target ="_blank" >
                    {{inItemTwo.meta.title}}
                  </a>
                </a-menu-item>
                <!-- 三层有children子级 -->
                <!--<a-sub-menu v-if="inItemTwo.children"  :key="inItemTwo.id">
                    <span slot="title">
                      <span>
                          {{inItemTwo.meta.title}}
                      </span>
                    </span>
                     <template v-for="inItemThree in inItemTwo.children"> -->
                      <!-- 四层没children子级 -->
                      <!-- <a-menu-item v-if="!inItemThree.children" :disabled="inItemThree.disabled"  :key="inItemThree.key">
                        {{inItemThree.name}}
                      </a-menu-item>
                    </template> 
                </a-sub-menu>-->
              </template>
            </a-sub-menu>
          </template>
        </a-sub-menu>
      </template>
    </a-menu>
</template>
<script>
  import AppLink from './Link'
  export default {
    name: 'subMenu',
    components: {
        AppLink
    },
    props: {
        // ...Menu.SubMenu.props,
        menuInfo: {
            type: Array,
            default: () => [],
        },
    },
    data(){
      return{
        chooseValue: {},
      }
    },
    created(){
      console.log(this.menuInfo)
    },
    methods:{
      	// 这里只能拿到他的key,包含在e里面
      switchMenu (e) {
          // this.getTreeNoteInfo(e.key)
          console.log(e)
      },
      //测试递归处理数据,此处需求无用,可忽略
      getTreeNoteInfo (key) {
        console.log(key)
        this.chooseValue = {}  // 这是我想要拿到的节点信息,一层一层的对比key去找
        this.menuInfo.forEach((item) => {
          item.children.forEach(inItem => {
            // 第二层有数据的
            if (inItem.id === key) {
              this.chooseValue = JSON.parse(JSON.stringify(inItem))
            } else {
              if (inItem.children) {
                inItem.children.forEach(inItemTwo => {
                  // 第三层有数据
                  if (inItemTwo.id === key) {
                    this.chooseValue = JSON.parse(JSON.stringify(inItemTwo))
                  } else {
                    if (inItemTwo.children) {
                      inItemTwo.children.forEach(inItemThree => {
                        // 第四层有数据的
                        if (inItemThree.id === key) {
                          this.chooseValue = JSON.parse(JSON.stringify(inItemThree))
                        }
                      })
                    }
                  }
                })
              }
            }
          })
        })
      }
    }
  }

</script>
<style lang="less" scoped>
  .navlist ul{ margin-right: 20px; }
  .ant-menu-light{ min-height: auto !important;}
  .ant-menu-horizontal{ background: #3E6CD6; color: #fff; border: 0; }
  .ant-menu-submenu-title:hover{ border: none }
  .ant-menu-horizontal > .ant-menu-item:hover, .ant-menu-horizontal > .ant-menu-submenu:hover, .ant-menu-horizontal > .ant-menu-item-active, .ant-menu-horizontal > .ant-menu-submenu-active, .ant-menu-horizontal > .ant-menu-item-open, .ant-menu-horizontal > .ant-menu-submenu-open, .ant-menu-horizontal > .ant-menu-item-selected, .ant-menu-horizontal > .ant-menu-submenu-selected{
    border-bottom: 2px solid #3E6CD6; color: #fff;
  }
</style>

4.父级组件引用

<think>嗯,用户想在Ant Design Vue中实现级联选择器的单选功能和选择后的回显。首先,我需要回忆一下Ant Design Vue级联选择器组件,也就是a-cascader的用法。默认情况下,这个组件支持单选还是多选呢?我记得可能需要设置某个属性,比如是否多选,可能需要设置multiple属性为false或者不设置,因为默认可能是单选。不过用户提到的是单选,所以可能需要确认一下。 接下来是回显的问题。当用户选择了一个选项后,如何将选中的值显示在组件上。通常这需要绑定v-model到一个变量,当选择后,该变量会被更新,从而自动显示选中的值。但是级联选择器的数据是层级结构的,所以绑定的值应该是一个数组,每个元素对应每一级的选项值。比如选择了省份-城市-区域,那么数组可能是['省份ID', '城市ID', '区域ID']。 但是用户可能需要动态加载子选项,比如异步加载数据。这时候需要使用load-data属性,并设置:load-data="true",然后定义加载数据的方法。例如,当用户选择某一级时,动态获取下一级的数据。 然后,用户可能需要设置change-on-select,这样在点击某一级时就可以选中,而不需要选到最后一级。不过这个属性可能在Ant Design Vue中是否有不同的名称?比如是否叫做changeOnSelect,或者在组件中需要用驼峰式或者短横线分隔?需要查阅文档确认。 另外,回显的时候,如果选项是动态加载的,可能需要预先加载所有层级的数据,否则可能无法显示完整的路径。比如,如果数据是异步加载的,那么在回显时,需要确保各级的数据都已经加载完毕,否则可能显示的是ID而不是标签。这时候可能需要使用field-names来自定义字段名,或者确保数据格式正确。 还有,用户提到的选项回显可能需要将选中的值转换为对应的标签显示出来。例如,选中的值是['zhejiang', 'hangzhou'],需要显示为“浙江省 / 杭州市”。这时候可能需要手动处理,或者使用组件自带的显示功能。可能需要查看a-cascader的文档,看看是否有相关的属性或插槽可以使用。 然后,用户可能需要示例代码,包括模板部分和数据方法部分。在模板中,需要定义a-cascader组件,绑定v-model到selectedKeys,设置options为数据源,可能还需要设置其他属性如placeholder、change-on-select(或者类似属性)、是否显示搜索框等。在脚本部分,需要定义options数据,可能是一个嵌套数组,每个节点有value、label、children等属性。同时,可能需要处理change事件,在选择时更新selectedKeys和显示标签。 如果涉及到异步加载子节点,可能需要使用loadData方法,在方法中根据当前选中的节点动态加载下一级的数据,并更新options。这时候需要注意数据的合并,避免覆盖已有的数据。 还有,用户提到的引用中有提到element的el-cascader无法直接支持一级多选,二级单选,但用户的问题是关于Ant Design Vue的,所以可能需要确认a-cascader是否支持类似的需求,或者需要自定义实现。 总结步骤应该是: 1. 引入a-cascader组件,设置v-model绑定选中的值。 2. 配置options数据源,结构符合层级要求。 3. 设置change-on-select属性,允许选择任意一级。 4. 处理change事件,更新选中的值和显示的标签。 5. 回显时,确保selectedKeys是正确的层级数组,并且options包含对应的数据,以便正确显示标签。 可能遇到的问题包括动态加载数据时的回显问题,需要确保在回显时所有层级的选项都已经加载到options中,否则可能无法正确显示标签。这时候可能需要初始化时预先加载所有必要的数据,或者在回显时动态加载缺失的数据。 另外,是否需要使用displayRender属性来自定义显示格式?例如,将选中的值数组转换为字符串显示。这可能在回显时有用,确保显示的是标签而不是值。 最后,根据用户提供的引用内容,虽然引用的内容是关于element和uniapp的,但可能需要忽略这些,专注于Ant Design Vue的实现。不过引用中的问题可能暗示用户需要处理多级联选择,并且需要自定义一些功能,比如部分层级多选,但用户当前的问题只是单选,所以可能不需要处理多选的情况。</think>以下是在Ant Design Vue中实现级联选择器单选及回显的完整实现方案: ### 一、基础实现步骤 1. **组件引入**:使用`a-cascader`组件并绑定必要属性 ```vue <template> <a-cascader v-model:value="selectedKeys" :options="options" :load-data="loadData" change-on-select placeholder="请选择" @change="handleChange" /> </template> ``` 2. **数据结构配置**(支持异步加载时需包含`isLeaf`标识): ```javascript const options = ref([ { value: 'zhejiang', label: '浙江省', isLeaf: false, }, { value: 'jiangsu', label: '江苏省', isLeaf: false, } ]); ``` ### 二、关键功能实现 1. **异步加载子项**(通过`loadData`方法实现动态加载): ```javascript const loadData = async (selectedOptions) => { const targetOption = selectedOptions[selectedOptions.length - 1]; targetOption.loading = true; // 模拟异步请求 const res = await mockAPIRequest(targetOption.value); targetOption.children = res.data.map(item => ({ value: item.id, label: item.name, isLeaf: item.level === 3 // 假设第三级是末级 })); targetOption.loading = false; options.value = [...options.value]; }; ``` 2. **选择结果处理**: ```javascript const selectedKeys = ref([]); const displayText = ref(''); const handleChange = (value, selectedOptions) => { displayText.value = selectedOptions.map(o => o.label).join(' / '); console.log('当前选择路径:', value); }; ``` ### 三、数据回显实现 当需要显示已存储的路径值时: ```javascript // 假设已存储的值为 ['zhejiang', 'hangzhou', 'xihu'] const initDisplay = async (storedValue) => { selectedKeys.value = storedValue; // 动态加载完整路径数据 let currentOptions = options.value; for (const val of storedValue) { const target = currentOptions.find(opt => opt.value === val); if (target && !target.children) { await loadData([target]); } currentOptions = target?.children || []; } }; ``` ### 四、样式优化建议 ```vue <style scoped> /* 自定义下拉箭头 */ .ant-cascader-picker-arrow { color: #1890ff; } /* 调整输入框高度 */ .ant-input-affix-wrapper { height: 40px; } </style> ``` ### 五、完整组件示例 ```vue <script setup> import { ref } from 'vue'; const options = ref([...]); // 初始数据 const selectedKeys = ref([]); const displayText = ref(''); const loadData = async (selectedOptions) => { // 异步加载逻辑 }; const handleChange = (value, selectedOptions) => { // 处理选择结果 }; </script> <template> <div class="cascader-wrapper"> <a-cascader v-model:value="selectedKeys" :options="options" :load-data="loadData" change-on-select @change="handleChange" /> <div v-if="displayText" class="selected-text"> 当前选择:{{ displayText }} </div> </div> </template> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木易66丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值